diff --git a/go.mod b/go.mod index 806d22309b..4bb70a77a9 100644 --- a/go.mod +++ b/go.mod @@ -45,14 +45,14 @@ require ( github.com/linuxkit/virtsock v0.0.0-20241009230534-cb6a20cc0422 github.com/mattn/go-shellwords v1.0.13 github.com/moby/sys/user v0.4.0 - github.com/open-policy-agent/opa v1.20.1 + github.com/open-policy-agent/opa v1.21.1 github.com/opencontainers/cgroups v0.1.0 github.com/opencontainers/runc v1.4.3 github.com/opencontainers/runtime-spec v1.3.0 github.com/pelletier/go-toml v1.9.5 github.com/pkg/errors v0.9.1 github.com/samber/lo v1.53.0 - github.com/sirupsen/logrus v1.10.0 + github.com/sirupsen/logrus v1.10.2 github.com/urfave/cli v1.22.17 github.com/urfave/cli/v2 v2.27.7 github.com/vishvananda/netlink v1.3.1 @@ -88,21 +88,21 @@ require ( github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-logr/logr v1.4.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/gobwas/glob v0.2.3 // indirect + github.com/gobwas/glob v1.0.0 // indirect github.com/goccy/go-json v0.10.6 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/klauspost/compress v1.19.2 // indirect + github.com/klauspost/compress v1.20.0 // indirect github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect github.com/lestrrat-go/blackmagic v1.0.4 // indirect - github.com/lestrrat-go/dsig v1.2.1 // indirect + github.com/lestrrat-go/dsig v1.4.0 // indirect github.com/lestrrat-go/dsig-secp256k1 v1.0.0 // indirect github.com/lestrrat-go/httpcc v1.0.1 // indirect - github.com/lestrrat-go/httprc/v3 v3.0.5 // indirect + github.com/lestrrat-go/httprc/v3 v3.0.6 // indirect github.com/lestrrat-go/iter v1.0.2 // indirect github.com/lestrrat-go/jwx v1.2.31 // indirect - github.com/lestrrat-go/jwx/v3 v3.1.1 // indirect + github.com/lestrrat-go/jwx/v3 v3.3.0 // indirect github.com/lestrrat-go/option v1.0.1 // indirect github.com/lestrrat-go/option/v2 v2.0.0 // indirect github.com/mdlayher/socket v0.5.1 // indirect @@ -120,7 +120,7 @@ require ( github.com/segmentio/asm v1.2.1 // indirect github.com/tchap/go-patricia/v2 v2.3.3 // indirect github.com/valyala/fastjson v1.6.10 // indirect - github.com/vektah/gqlparser/v2 v2.5.36 // indirect + github.com/vektah/gqlparser/v2 v2.5.37 // indirect github.com/veraison/go-cose v1.3.0 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect @@ -129,7 +129,6 @@ require ( github.com/yashtewari/glob-intersection v0.2.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/otel/metric v1.46.0 // indirect - go.yaml.in/yaml/v2 v2.4.4 // indirect go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/crypto v0.57.0 // indirect golang.org/x/mod v0.41.0 // indirect @@ -137,7 +136,6 @@ require ( golang.org/x/tools v0.49.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260825221802-da73d73af1c5 // indirect google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.2 // indirect - sigs.k8s.io/yaml v1.6.0 // indirect ) replace google.golang.org/genproto => google.golang.org/genproto v0.0.0-20250428153025-10db94c68c34 diff --git a/go.sum b/go.sum index c610de0434..219354ea89 100644 --- a/go.sum +++ b/go.sum @@ -481,8 +481,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 h1:5RVFMOWjMyRy8cARdy79nAmgYw3hK/4HUq48LQ6Wwqo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= -github.com/dgraph-io/badger/v4 v4.9.5 h1:zT46OMrF3ntqsfI3ynKp7hUkQrGlcK2CX5psQmH0iW0= -github.com/dgraph-io/badger/v4 v4.9.5/go.mod h1:Xa9dAupjbwAacupWFCpa6YEn9E1PjBXkfZYr2I/8aWg= +github.com/dgraph-io/badger/v4 v4.9.6 h1:IQqMPVGLNCQr1b4Mu8lHkYm/xyqFRsyKaFEtyLi9CCQ= +github.com/dgraph-io/badger/v4 v4.9.6/go.mod h1:Xa9dAupjbwAacupWFCpa6YEn9E1PjBXkfZYr2I/8aWg= github.com/dgraph-io/ristretto/v2 v2.2.0 h1:bkY3XzJcXoMuELV8F+vS8kzNgicwQFAaGINAEJdWGOM= github.com/dgraph-io/ristretto/v2 v2.2.0/go.mod h1:RZrm63UmcBAaYWC1DotLYBmTvgkrs0+XhBd7Npn7/zI= github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo= @@ -569,8 +569,8 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+ github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-quicktest/qt v1.101.1-0.20240301121107-c6c8733fa1e6 h1:teYtXy9B7y5lHTp8V9KPxpYRAVA7dozigQcMiBust1s= github.com/go-quicktest/qt v1.101.1-0.20240301121107-c6c8733fa1e6/go.mod h1:p4lGIVX+8Wa6ZPNDvqcxq36XpUDLh42FLetFU7odllI= -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/gobwas/glob v1.0.0 h1:p+FKbLEIsK1yZ39/OINwFvqNb5oyPY4H8xcy6uYu8dg= +github.com/gobwas/glob v1.0.0/go.mod h1:oWCdo522i2P1n/hMXGNWs7yoV4wy/ciZuUIbvKj5rkc= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU= github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= @@ -759,8 +759,8 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/klauspost/compress v1.19.2 h1:hMRETovs/pu/dVWN7zIT1PGG8t509MwT6bO7XSi26R8= -github.com/klauspost/compress v1.19.2/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA= +github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= @@ -778,20 +778,20 @@ github.com/lestrrat-go/backoff/v2 v2.0.8 h1:oNb5E5isby2kiro9AgdHLv5N5tint1AnDVVf github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y= github.com/lestrrat-go/blackmagic v1.0.4 h1:IwQibdnf8l2KoO+qC3uT4OaTWsW7tuRQXy9TRN9QanA= github.com/lestrrat-go/blackmagic v1.0.4/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw= -github.com/lestrrat-go/dsig v1.2.1 h1:MwxzZhE4+4fguHi+uDALKVlC3Cn+O1QU1Q/F8D7hVIc= -github.com/lestrrat-go/dsig v1.2.1/go.mod h1:RD2eOaidyPvpc7IJQoO3Qq52RWdy8ZcJs8lrOnoa1Kc= +github.com/lestrrat-go/dsig v1.4.0 h1:g7LUjK8cT74A5DzBXJI5HzsJuLhoYN0Wzj4nuOMIrH8= +github.com/lestrrat-go/dsig v1.4.0/go.mod h1:I8Nddg/vN2cUl/h8N7SRRApLnNNeyZPIqLYpvpOtGGo= github.com/lestrrat-go/dsig-secp256k1 v1.0.0 h1:JpDe4Aybfl0soBvoVwjqDbp+9S1Y2OM7gcrVVMFPOzY= github.com/lestrrat-go/dsig-secp256k1 v1.0.0/go.mod h1:CxUgAhssb8FToqbL8NjSPoGQlnO4w3LG1P0qPWQm/NU= github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= -github.com/lestrrat-go/httprc/v3 v3.0.5 h1:S+Mb4L2I+bM6JGTibLmxExhyTOqnXjqx+zi9MoXw/TM= -github.com/lestrrat-go/httprc/v3 v3.0.5/go.mod h1:mSMtkZW92Z98M5YoNNztbRGxbXHql7tSitCvaxvo9l0= +github.com/lestrrat-go/httprc/v3 v3.0.6 h1:4FpLQ18KK/ypPbVU3NLWJNRvH3kcYiqKqWfKGqNWxxI= +github.com/lestrrat-go/httprc/v3 v3.0.6/go.mod h1:mSMtkZW92Z98M5YoNNztbRGxbXHql7tSitCvaxvo9l0= github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= github.com/lestrrat-go/jwx v1.2.31 h1:/OM9oNl/fzyldpv5HKZ9m7bTywa7COUfg8gujd9nJ54= github.com/lestrrat-go/jwx v1.2.31/go.mod h1:eQJKoRwWcLg4PfD5CFA5gIZGxhPgoPYq9pZISdxLf0c= -github.com/lestrrat-go/jwx/v3 v3.1.1 h1:yd9AdPmZ4INnQ7k42IrzXYpnEG803+SrQ6hdMvzHJzw= -github.com/lestrrat-go/jwx/v3 v3.1.1/go.mod h1:uw/MN2M/Xiu4FhwcIwH11Zsh9JWx9SWzgALl7/uIEkU= +github.com/lestrrat-go/jwx/v3 v3.3.0 h1:OXcYvQOQ7cxWzeZ/Q9sYk8ABe/kCSI371WmuACiCT+4= +github.com/lestrrat-go/jwx/v3 v3.3.0/go.mod h1:eIJhDcKHBwcgxqv8RiIylV67TVl1wJp/265IAHY1Db8= github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= @@ -841,8 +841,8 @@ github.com/mrunalp/fileutils v0.5.1 h1:F+S7ZlNKnrwHfSwdlgNSkKo67ReVf8o9fel6C3dkm github.com/mrunalp/fileutils v0.5.1/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= 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/open-policy-agent/opa v1.20.1 h1:wSC3zjHbRyt7X3daV/DsjnhDywzB3l0m0gWhgX1W2vQ= -github.com/open-policy-agent/opa v1.20.1/go.mod h1:pxxSP1noAirD8UJ7PgAjoRw39IE0Bk/JRFkUP3+51lU= +github.com/open-policy-agent/opa v1.21.1 h1:j6NIMLmdOPUTp9+1fgtWLqbOPqwkTaxNm4T3ngtUB48= +github.com/open-policy-agent/opa v1.21.1/go.mod h1:eJL6KUOIaW5YLnhJEA6sm3FOYRDJaHZvYT6geATbpPk= github.com/opencontainers/cgroups v0.1.0 h1:6W05KiDvgj1yi/6D13SJCvYx8TAESz67szvHPxhfcrs= github.com/opencontainers/cgroups v0.1.0/go.mod h1:hPBRvnBhLZueEN0eJyozMeM3HeFGYlZW9KnO//px6G4= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= @@ -879,8 +879,8 @@ github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJ github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= -github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/client_model v0.6.3 h1:O0jaTVAYNxTHYInEPFJt5I3+sN8zqBtVMPTB1qyxiEo= +github.com/prometheus/client_model v0.6.3/go.mod h1:gpN5P9S7Rr6Yr92PiQ+Ixvhf6JZEkF1dnxsYL2aPBEM= github.com/prometheus/common v0.70.1 h1:1HvjP4D5oL3t8RsPlwxA9onvvStjtIHYE5XuuwOi/PY= github.com/prometheus/common v0.70.1/go.mod h1:VdFUQDMZK3VLkurFUVhia6uys/0suUp86TJz5qbJRhc= github.com/prometheus/procfs v0.21.1 h1:GljZCt+zSTS+NZq88cyQ1LjZ+RCHp3uVuabBWA5+OJI= @@ -895,8 +895,8 @@ github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/f github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= -github.com/rogpeppe/go-internal v1.15.0 h1:D0RCU5rMAp+SpgkiNdrjfJ+LX4J1M32V2NeCY7EJ6hc= -github.com/rogpeppe/go-internal v1.15.0/go.mod h1:DrUVZyrJU+txYW5/1kwtXQSMFio52ZOxX7yM1VHvnxs= +github.com/rogpeppe/go-internal v1.16.0 h1:O9DK+vNMDVGLr2BeZqmpLeMjiMNkuXfcqntWbZV6S5g= +github.com/rogpeppe/go-internal v1.16.0/go.mod h1:DrUVZyrJU+txYW5/1kwtXQSMFio52ZOxX7yM1VHvnxs= 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/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= @@ -907,8 +907,8 @@ github.com/seccomp/libseccomp-golang v0.11.1 h1:wuk4ZjSx6kyQII4rj6G6fvVzRHQaSiPv github.com/seccomp/libseccomp-golang v0.11.1/go.mod h1:5m1Lk8E9OwgZTTVz4bBOer7JuazaBa+xTkM895tDiWc= github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0= github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= -github.com/sirupsen/logrus v1.10.0 h1:T8MxJJXVZkfcC5zSRMRAg2F8+lxjmUCGGWPzFxO+Msc= -github.com/sirupsen/logrus v1.10.0/go.mod h1:FXZFonkDAnFozmO+5hGAFvB0Yg9/j2SIhA/QuIkP180= +github.com/sirupsen/logrus v1.10.2 h1:G2SED73/qrAu6YwbdxOD6peLkCBI3z7L+ykJFTXJBBo= +github.com/sirupsen/logrus v1.10.2/go.mod h1:SLEg8TqYulVKKfIGHldVp2K2aYz2DKSVBq4g/H5bR7Q= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= @@ -950,8 +950,8 @@ github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU= github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4= github.com/valyala/fastjson v1.6.10 h1:/yjJg8jaVQdYR3arGxPE2X5z89xrlhS0eGXdv+ADTh4= github.com/valyala/fastjson v1.6.10/go.mod h1:e6FubmQouUNP73jtMLmcbxS6ydWIpOfhz34TSfO3JaE= -github.com/vektah/gqlparser/v2 v2.5.36 h1:CN9mKVHgMkc+XftdOWIhb4HEL8wKSYkFAqhf8booa7s= -github.com/vektah/gqlparser/v2 v2.5.36/go.mod h1:cAJ9qwVgPaUkWv6Gn8vn0mqOE0Ui5Pn56wNy5396XWo= +github.com/vektah/gqlparser/v2 v2.5.37 h1:jbb1Ilv+xBklV6653tKb4oVUupPNTLb5LmrnBKVI12Y= +github.com/vektah/gqlparser/v2 v2.5.37/go.mod h1:9O4Ox6Ngd3Y12bMD3w6i3CRQXh8W1oC1q0m6olCymDM= github.com/veraison/go-cose v1.3.0 h1:2/H5w8kdSpQJyVtIhx8gmwPJ2uSz1PkyWFx0idbd7rk= github.com/veraison/go-cose v1.3.0/go.mod h1:df09OV91aHoQWLmy1KsDdYiagtXgyAwAl8vFeFn1gMc= github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW6bV0= @@ -1095,8 +1095,6 @@ go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= -go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= -go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -2100,5 +2098,3 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8 rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= -sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/test/go.mod b/test/go.mod index dd1993b9c4..f17c79f65a 100644 --- a/test/go.mod +++ b/test/go.mod @@ -19,7 +19,7 @@ require ( github.com/opencontainers/runtime-spec v1.3.0 github.com/opencontainers/runtime-tools v0.9.1-0.20260316125833-8a4db579f5c8 github.com/pkg/errors v0.9.1 - github.com/sirupsen/logrus v1.10.0 + github.com/sirupsen/logrus v1.10.2 github.com/urfave/cli/v2 v2.27.7 go.opentelemetry.io/otel v1.46.0 go.opentelemetry.io/otel/sdk v1.46.0 @@ -60,20 +60,20 @@ require ( github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-logr/logr v1.4.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/gobwas/glob v0.2.3 // indirect + github.com/gobwas/glob v1.0.0 // indirect github.com/goccy/go-json v0.10.6 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/klauspost/compress v1.19.2 // indirect + github.com/klauspost/compress v1.20.0 // indirect github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect github.com/lestrrat-go/blackmagic v1.0.4 // indirect - github.com/lestrrat-go/dsig v1.2.1 // indirect + github.com/lestrrat-go/dsig v1.4.0 // indirect github.com/lestrrat-go/dsig-secp256k1 v1.0.0 // indirect github.com/lestrrat-go/httpcc v1.0.1 // indirect - github.com/lestrrat-go/httprc/v3 v3.0.5 // indirect + github.com/lestrrat-go/httprc/v3 v3.0.6 // indirect github.com/lestrrat-go/iter v1.0.2 // indirect github.com/lestrrat-go/jwx v1.2.31 // indirect - github.com/lestrrat-go/jwx/v3 v3.1.1 // indirect + github.com/lestrrat-go/jwx/v3 v3.3.0 // indirect github.com/lestrrat-go/option v1.0.1 // indirect github.com/lestrrat-go/option/v2 v2.0.0 // indirect github.com/linuxkit/virtsock v0.0.0-20241009230534-cb6a20cc0422 // indirect @@ -86,7 +86,7 @@ require ( github.com/moby/sys/user v0.4.0 // indirect github.com/moby/sys/userns v0.1.0 // indirect github.com/mrunalp/fileutils v0.5.1 // indirect - github.com/open-policy-agent/opa v1.20.1 // indirect + github.com/open-policy-agent/opa v1.21.1 // indirect github.com/opencontainers/cgroups v0.1.0 // indirect github.com/opencontainers/runc v1.4.3 // indirect github.com/opencontainers/selinux v1.13.1 // indirect @@ -98,7 +98,7 @@ require ( github.com/segmentio/asm v1.2.1 // indirect github.com/tchap/go-patricia/v2 v2.3.3 // indirect github.com/valyala/fastjson v1.6.10 // indirect - github.com/vektah/gqlparser/v2 v2.5.36 // indirect + github.com/vektah/gqlparser/v2 v2.5.37 // indirect github.com/veraison/go-cose v1.3.0 // indirect github.com/vishvananda/netlink v1.3.1 // indirect github.com/vishvananda/netns v0.0.5 // indirect @@ -108,17 +108,15 @@ require ( github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect github.com/yashtewari/glob-intersection v0.2.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0 // indirect go.opentelemetry.io/otel/metric v1.46.0 // indirect go.opentelemetry.io/otel/trace v1.46.0 // indirect - go.yaml.in/yaml/v2 v2.4.4 // indirect go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/crypto v0.57.0 // indirect golang.org/x/net v0.59.0 // indirect golang.org/x/text v0.42.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260825221802-da73d73af1c5 // indirect gotest.tools/v3 v3.5.2 // indirect - sigs.k8s.io/yaml v1.6.0 // indirect ) replace github.com/Microsoft/hcsshim => ../ diff --git a/test/go.sum b/test/go.sum index eb10cec225..b6295a14ce 100644 --- a/test/go.sum +++ b/test/go.sum @@ -63,8 +63,8 @@ github.com/cyphar/filepath-securejoin v0.7.0/go.mod h1:ymLGms/u3BYaviIiuKFnUx8Ek github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 h1:5RVFMOWjMyRy8cARdy79nAmgYw3hK/4HUq48LQ6Wwqo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= -github.com/dgraph-io/badger/v4 v4.9.5 h1:zT46OMrF3ntqsfI3ynKp7hUkQrGlcK2CX5psQmH0iW0= -github.com/dgraph-io/badger/v4 v4.9.5/go.mod h1:Xa9dAupjbwAacupWFCpa6YEn9E1PjBXkfZYr2I/8aWg= +github.com/dgraph-io/badger/v4 v4.9.6 h1:IQqMPVGLNCQr1b4Mu8lHkYm/xyqFRsyKaFEtyLi9CCQ= +github.com/dgraph-io/badger/v4 v4.9.6/go.mod h1:Xa9dAupjbwAacupWFCpa6YEn9E1PjBXkfZYr2I/8aWg= github.com/dgraph-io/ristretto/v2 v2.2.0 h1:bkY3XzJcXoMuELV8F+vS8kzNgicwQFAaGINAEJdWGOM= github.com/dgraph-io/ristretto/v2 v2.2.0/go.mod h1:RZrm63UmcBAaYWC1DotLYBmTvgkrs0+XhBd7Npn7/zI= github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo= @@ -96,8 +96,8 @@ 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-quicktest/qt v1.101.1-0.20240301121107-c6c8733fa1e6 h1:teYtXy9B7y5lHTp8V9KPxpYRAVA7dozigQcMiBust1s= github.com/go-quicktest/qt v1.101.1-0.20240301121107-c6c8733fa1e6/go.mod h1:p4lGIVX+8Wa6ZPNDvqcxq36XpUDLh42FLetFU7odllI= -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/gobwas/glob v1.0.0 h1:p+FKbLEIsK1yZ39/OINwFvqNb5oyPY4H8xcy6uYu8dg= +github.com/gobwas/glob v1.0.0/go.mod h1:oWCdo522i2P1n/hMXGNWs7yoV4wy/ciZuUIbvKj5rkc= github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU= github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= @@ -120,8 +120,8 @@ github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtL github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/jsimonetti/rtnetlink/v2 v2.0.1 h1:xda7qaHDSVOsADNouv7ukSuicKZO7GgVUCXxpaIEIlM= github.com/jsimonetti/rtnetlink/v2 v2.0.1/go.mod h1:7MoNYNbb3UaDHtF8udiJo/RH6VsTKP1pqKLUTVCvToE= -github.com/klauspost/compress v1.19.2 h1:hMRETovs/pu/dVWN7zIT1PGG8t509MwT6bO7XSi26R8= -github.com/klauspost/compress v1.19.2/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA= +github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -130,20 +130,20 @@ github.com/lestrrat-go/backoff/v2 v2.0.8 h1:oNb5E5isby2kiro9AgdHLv5N5tint1AnDVVf github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y= github.com/lestrrat-go/blackmagic v1.0.4 h1:IwQibdnf8l2KoO+qC3uT4OaTWsW7tuRQXy9TRN9QanA= github.com/lestrrat-go/blackmagic v1.0.4/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw= -github.com/lestrrat-go/dsig v1.2.1 h1:MwxzZhE4+4fguHi+uDALKVlC3Cn+O1QU1Q/F8D7hVIc= -github.com/lestrrat-go/dsig v1.2.1/go.mod h1:RD2eOaidyPvpc7IJQoO3Qq52RWdy8ZcJs8lrOnoa1Kc= +github.com/lestrrat-go/dsig v1.4.0 h1:g7LUjK8cT74A5DzBXJI5HzsJuLhoYN0Wzj4nuOMIrH8= +github.com/lestrrat-go/dsig v1.4.0/go.mod h1:I8Nddg/vN2cUl/h8N7SRRApLnNNeyZPIqLYpvpOtGGo= github.com/lestrrat-go/dsig-secp256k1 v1.0.0 h1:JpDe4Aybfl0soBvoVwjqDbp+9S1Y2OM7gcrVVMFPOzY= github.com/lestrrat-go/dsig-secp256k1 v1.0.0/go.mod h1:CxUgAhssb8FToqbL8NjSPoGQlnO4w3LG1P0qPWQm/NU= github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= -github.com/lestrrat-go/httprc/v3 v3.0.5 h1:S+Mb4L2I+bM6JGTibLmxExhyTOqnXjqx+zi9MoXw/TM= -github.com/lestrrat-go/httprc/v3 v3.0.5/go.mod h1:mSMtkZW92Z98M5YoNNztbRGxbXHql7tSitCvaxvo9l0= +github.com/lestrrat-go/httprc/v3 v3.0.6 h1:4FpLQ18KK/ypPbVU3NLWJNRvH3kcYiqKqWfKGqNWxxI= +github.com/lestrrat-go/httprc/v3 v3.0.6/go.mod h1:mSMtkZW92Z98M5YoNNztbRGxbXHql7tSitCvaxvo9l0= github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= github.com/lestrrat-go/jwx v1.2.31 h1:/OM9oNl/fzyldpv5HKZ9m7bTywa7COUfg8gujd9nJ54= github.com/lestrrat-go/jwx v1.2.31/go.mod h1:eQJKoRwWcLg4PfD5CFA5gIZGxhPgoPYq9pZISdxLf0c= -github.com/lestrrat-go/jwx/v3 v3.1.1 h1:yd9AdPmZ4INnQ7k42IrzXYpnEG803+SrQ6hdMvzHJzw= -github.com/lestrrat-go/jwx/v3 v3.1.1/go.mod h1:uw/MN2M/Xiu4FhwcIwH11Zsh9JWx9SWzgALl7/uIEkU= +github.com/lestrrat-go/jwx/v3 v3.3.0 h1:OXcYvQOQ7cxWzeZ/Q9sYk8ABe/kCSI371WmuACiCT+4= +github.com/lestrrat-go/jwx/v3 v3.3.0/go.mod h1:eIJhDcKHBwcgxqv8RiIylV67TVl1wJp/265IAHY1Db8= github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= @@ -177,8 +177,8 @@ github.com/mrunalp/fileutils v0.5.1 h1:F+S7ZlNKnrwHfSwdlgNSkKo67ReVf8o9fel6C3dkm github.com/mrunalp/fileutils v0.5.1/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= 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/open-policy-agent/opa v1.20.1 h1:wSC3zjHbRyt7X3daV/DsjnhDywzB3l0m0gWhgX1W2vQ= -github.com/open-policy-agent/opa v1.20.1/go.mod h1:pxxSP1noAirD8UJ7PgAjoRw39IE0Bk/JRFkUP3+51lU= +github.com/open-policy-agent/opa v1.21.1 h1:j6NIMLmdOPUTp9+1fgtWLqbOPqwkTaxNm4T3ngtUB48= +github.com/open-policy-agent/opa v1.21.1/go.mod h1:eJL6KUOIaW5YLnhJEA6sm3FOYRDJaHZvYT6geATbpPk= github.com/opencontainers/cgroups v0.1.0 h1:6W05KiDvgj1yi/6D13SJCvYx8TAESz67szvHPxhfcrs= github.com/opencontainers/cgroups v0.1.0/go.mod h1:hPBRvnBhLZueEN0eJyozMeM3HeFGYlZW9KnO//px6G4= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= @@ -200,16 +200,16 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.24.1 h1:JnJkREXzWxUdCuPFpIWZiPispT9xVV59uiuyR2bPlnU= github.com/prometheus/client_golang v1.24.1/go.mod h1:F+oSRECHg4sse5ucfYpYDeIv/hu68Zo0uoHKetWnzcE= -github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= -github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/client_model v0.6.3 h1:O0jaTVAYNxTHYInEPFJt5I3+sN8zqBtVMPTB1qyxiEo= +github.com/prometheus/client_model v0.6.3/go.mod h1:gpN5P9S7Rr6Yr92PiQ+Ixvhf6JZEkF1dnxsYL2aPBEM= github.com/prometheus/common v0.70.1 h1:1HvjP4D5oL3t8RsPlwxA9onvvStjtIHYE5XuuwOi/PY= github.com/prometheus/common v0.70.1/go.mod h1:VdFUQDMZK3VLkurFUVhia6uys/0suUp86TJz5qbJRhc= github.com/prometheus/procfs v0.21.1 h1:GljZCt+zSTS+NZq88cyQ1LjZ+RCHp3uVuabBWA5+OJI= github.com/prometheus/procfs v0.21.1/go.mod h1:aB55Cww9pdSJVHk0hUf0inxWyyjPogFIjmHKYgMKmtY= github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 h1:bsUq1dX0N8AOIL7EB/X911+m4EHsnWEHeJ0c+3TTBrg= github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rogpeppe/go-internal v1.15.0 h1:D0RCU5rMAp+SpgkiNdrjfJ+LX4J1M32V2NeCY7EJ6hc= -github.com/rogpeppe/go-internal v1.15.0/go.mod h1:DrUVZyrJU+txYW5/1kwtXQSMFio52ZOxX7yM1VHvnxs= +github.com/rogpeppe/go-internal v1.16.0 h1:O9DK+vNMDVGLr2BeZqmpLeMjiMNkuXfcqntWbZV6S5g= +github.com/rogpeppe/go-internal v1.16.0/go.mod h1:DrUVZyrJU+txYW5/1kwtXQSMFio52ZOxX7yM1VHvnxs= 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/samber/lo v1.53.0 h1:t975lj2py4kJPQ6haz1QMgtId2gtmfktACxIXArw3HM= @@ -218,8 +218,8 @@ github.com/seccomp/libseccomp-golang v0.11.1 h1:wuk4ZjSx6kyQII4rj6G6fvVzRHQaSiPv github.com/seccomp/libseccomp-golang v0.11.1/go.mod h1:5m1Lk8E9OwgZTTVz4bBOer7JuazaBa+xTkM895tDiWc= github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0= github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= -github.com/sirupsen/logrus v1.10.0 h1:T8MxJJXVZkfcC5zSRMRAg2F8+lxjmUCGGWPzFxO+Msc= -github.com/sirupsen/logrus v1.10.0/go.mod h1:FXZFonkDAnFozmO+5hGAFvB0Yg9/j2SIhA/QuIkP180= +github.com/sirupsen/logrus v1.10.2 h1:G2SED73/qrAu6YwbdxOD6peLkCBI3z7L+ykJFTXJBBo= +github.com/sirupsen/logrus v1.10.2/go.mod h1:SLEg8TqYulVKKfIGHldVp2K2aYz2DKSVBq4g/H5bR7Q= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -233,8 +233,8 @@ github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU= github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4= github.com/valyala/fastjson v1.6.10 h1:/yjJg8jaVQdYR3arGxPE2X5z89xrlhS0eGXdv+ADTh4= github.com/valyala/fastjson v1.6.10/go.mod h1:e6FubmQouUNP73jtMLmcbxS6ydWIpOfhz34TSfO3JaE= -github.com/vektah/gqlparser/v2 v2.5.36 h1:CN9mKVHgMkc+XftdOWIhb4HEL8wKSYkFAqhf8booa7s= -github.com/vektah/gqlparser/v2 v2.5.36/go.mod h1:cAJ9qwVgPaUkWv6Gn8vn0mqOE0Ui5Pn56wNy5396XWo= +github.com/vektah/gqlparser/v2 v2.5.37 h1:jbb1Ilv+xBklV6653tKb4oVUupPNTLb5LmrnBKVI12Y= +github.com/vektah/gqlparser/v2 v2.5.37/go.mod h1:9O4Ox6Ngd3Y12bMD3w6i3CRQXh8W1oC1q0m6olCymDM= github.com/veraison/go-cose v1.3.0 h1:2/H5w8kdSpQJyVtIhx8gmwPJ2uSz1PkyWFx0idbd7rk= github.com/veraison/go-cose v1.3.0/go.mod h1:df09OV91aHoQWLmy1KsDdYiagtXgyAwAl8vFeFn1gMc= github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW6bV0= @@ -255,8 +255,8 @@ github.com/yashtewari/glob-intersection v0.2.0 h1:8iuHdN88yYuCzCdjt0gDe+6bAhUwBe github.com/yashtewari/glob-intersection v0.2.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 h1:8tvICD4vSTOOsNrsI4Ljf6C+6UKvpTEH5XY3JMoyPoo= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0/go.mod h1:z9+yiacE0IHRqM4qFfkbt/JYlmYXgss8GY/jXoNuPJI= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0 h1:3g7B90UzBltIDKq1/5mrTGxTnOFDV0ICOhLoxiZ8jlg= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0/go.mod h1:Ef8SuTh59BT7+ofpDxN9z+yOlc4t2GjLmKDgYNJL/NU= go.opentelemetry.io/otel v1.46.0 h1:FHt5/CDyVxi/8IM1CH7VE/rRgq3kLHa2mSTVMO8AWyc= go.opentelemetry.io/otel v1.46.0/go.mod h1:Gj3SEScelsNC45tp4nSxRYlS+f5iez7W8XPMCt905kE= go.opentelemetry.io/otel/metric v1.46.0 h1:yBnkXvgV7AXFILZc5K6IZe/CBFF3OS7BJ8ov6/lj0K8= @@ -271,8 +271,6 @@ go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= -go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= -go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M= @@ -310,5 +308,3 @@ k8s.io/cri-api v0.37.0 h1:8a3Rpw5NNRivF+q8GO/ECK3BrWT06M4Er8ndzLvTF7k= k8s.io/cri-api v0.37.0/go.mod h1:6V8Gb6wznJYVPToj8CLxgbxsFeeGIswNZCE7cy+xT90= k8s.io/cri-client v0.37.0 h1:X1TEEx74zCwqDoVnJ4OIXtrQLHY21AOe8vXbBNSOb40= k8s.io/cri-client v0.37.0/go.mod h1:x/nROOizpdhU/VGcokYhxtWioDPMhqinRRCHWOoVL2c= -sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= -sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/vendor/github.com/gobwas/glob/.gitignore b/vendor/github.com/gobwas/glob/.gitignore index b4ae623be5..534b466c5b 100644 --- a/vendor/github.com/gobwas/glob/.gitignore +++ b/vendor/github.com/gobwas/glob/.gitignore @@ -6,3 +6,5 @@ glob.iml *.dot *.png *.svg +patterns.txt +*.bench diff --git a/vendor/github.com/gobwas/glob/.travis.yml b/vendor/github.com/gobwas/glob/.travis.yml deleted file mode 100644 index e8a276826c..0000000000 --- a/vendor/github.com/gobwas/glob/.travis.yml +++ /dev/null @@ -1,9 +0,0 @@ -sudo: false - -language: go - -go: - - 1.5.3 - -script: - - go test -v ./... diff --git a/vendor/github.com/gobwas/glob/bench.sh b/vendor/github.com/gobwas/glob/bench.sh index 804cf22e64..8570697b88 100644 --- a/vendor/github.com/gobwas/glob/bench.sh +++ b/vendor/github.com/gobwas/glob/bench.sh @@ -1,26 +1,41 @@ -#! /bin/bash +#!/bin/bash +# +# Compares the benchmarks of the current branch against a git revision: +# +# ./bench.sh v0.2.3 # all the benchmarks +# ./bench.sh master 'Match' # the ones matching a -bench regexp +# +# The results are written to *.bench files in the current directory and +# compared with benchstat (go install golang.org/x/perf/cmd/benchstat@latest). -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 -} +set -eu + +prev=$1 +what=${2:-.} +curr=$(git rev-parse --abbrev-ref HEAD) +rnd=$(head -c4 "$out" + echo "OK" + git checkout -q "$curr" + sleep 5 +} -bench ${to} $2 -bench ${current} $2 +bench "$prev" +bench "$curr" -benchcmp $3 "/tmp/${to}-$2.bench" "/tmp/${current}-$2.bench" +benchstat "$(file "$prev")" "$(file "$curr")" diff --git a/vendor/github.com/gobwas/glob/compiler/compiler.go b/vendor/github.com/gobwas/glob/compiler/compiler.go deleted file mode 100644 index 02e7de80a0..0000000000 --- a/vendor/github.com/gobwas/glob/compiler/compiler.go +++ /dev/null @@ -1,525 +0,0 @@ -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 -} diff --git a/vendor/github.com/gobwas/glob/glob.go b/vendor/github.com/gobwas/glob/glob.go index 2afde343af..fb3e475546 100644 --- a/vendor/github.com/gobwas/glob/glob.go +++ b/vendor/github.com/gobwas/glob/glob.go @@ -1,80 +1,158 @@ package glob import ( - "github.com/gobwas/glob/compiler" + "fmt" + + "github.com/gobwas/glob/internal/debug" "github.com/gobwas/glob/syntax" ) -// Glob represents compiled glob pattern. -type Glob interface { - Match(string) bool +// SyntaxError is returned by [Compile] when the given pattern can not be +// parsed. Offset points at the place in the pattern the error was detected +// at, so the tooling can do things like: +// +// {a,b +// ----^ unclosed `{` +type SyntaxError struct { + // Offset is a byte offset in the pattern. + Offset int + // Reason describes the error. + Reason string } -// Compile creates Glob for given pattern and strings (if any present after pattern) as separators. -// The pattern syntax is: +func (s *SyntaxError) Error() string { + return fmt.Sprintf("glob: syntax error at %d: %s", s.Offset, s.Reason) +} + +// Pattern represents a compiled glob pattern. // -// pattern: -// { term } +// A pattern is compiled into a tree of matchers: // -// 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 +// `a` => "a" +// `a*` => ["a"·*] +// `{a*,b}` => {["a"·*]|"b"} // -// character-range: -// c matches character c (c != `\\`, `-`, `]`) -// `\` c matches character c -// lo `-` hi matches character c for lo <= c <= hi +// Matching is a backtracking walk over that tree; see [Pattern.Match]. +type Pattern struct { + // str is the pattern text the Pattern was compiled from; see + // [Pattern.String]. + str string + + // sep are the separators the Pattern was compiled with; see + // [Pattern.Separators]. + sep []rune + + // m is the root of the matcher tree; see [matcher]. + m matcher + + // state tells whether matching m needs the backtracking state, that + // is, whether it may save checkpoints; see [needsState]. A pattern + // without them is matched with a plain call chain. + state bool + + // The match preconditions: every matching string is at least minLen + // bytes and ends with suffix. They fail the obvious mismatches in O(1) + // instead of a backtracking walk -- e.g. `a*a*a*b` requires the + // trailing `b`, no matter how the stars go. + // + // A precondition pays off only when it catches a mismatch earlier than + // the walk would, which is why: + // + // - there is no required prefix: the walk is left-to-right, so a + // leading literal is the first thing checked anyway, while a bad + // suffix or length is discovered last, after the whole + // backtracking exploration; + // + // - they are computed for the stateful patterns only: a stateless + // pattern is a plain call chain whose matchers perform these very + // checks themselves (e.g. suffixMatcher is a HasSuffix), so the + // precondition would only duplicate them. + minLen int + suffix string +} + +// String returns the source text used to compile the pattern, the same way +// [regexp.Regexp.String] does. // -// pattern-list: -// pattern { `,` pattern } -// comma-separated (without spaces) patterns +// Note that separators are not part of String: they are given to Compile +// alongside the pattern text. +func (p *Pattern) String() string { + return p.str +} + +// Separators returns the separators the pattern was compiled with, in the +// order they were given to Compile; nil when there are none. // -func Compile(pattern string, separators ...rune) (Glob, error) { - ast, err := syntax.Parse(pattern) - if err != nil { - return nil, err - } +// The returned slice is the very one given to Compile, sharing its backing +// array: it is not copied on the way in or out. Matching does not use it. +func (p *Pattern) Separators() []rune { + return p.sep +} - matcher, err := compiler.Compile(ast, separators) - if err != nil { - return nil, err +func init() { + // The matcher tree is unexported; hand its rendering to the in-module + // tooling (cmd/globtest -v) without widening the public API. + debug.Tree = func(p any) string { + return p.(*Pattern).m.String() } +} - return matcher, nil +// Compile compiles the glob pattern. The separators, if given, are the +// characters `*` and `?` do not match (`**` does); they can not be changed +// after the compilation, see [Pattern.Separators]. A malformed pattern is +// reported with a [*SyntaxError]. +// +// 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 +// `[` [ `!` ] class `]` +// character class; `!` negates it +// `{` pattern-list `}` +// pattern alternatives +// c matches character c (c != `*`, `**`, `?`, `\`, `[`, `{`, `}`) +// `\` c matches character c +// +// class: +// lo `-` hi matches character c for lo <= c <= hi +// { c } matches any of the listed characters (c != `\`, `]`; +// `\` c matches c, `-` is literal here); must be non-empty +// +// pattern-list: +// pattern { `,` pattern } +// comma-separated (without spaces) patterns +func Compile(pattern string, separators ...rune) (*Pattern, error) { + return compile(pattern, separators) } -// MustCompile is the same as Compile, except that if Compile returns error, this will panic -func MustCompile(pattern string, separators ...rune) Glob { +// MustCompile is the same as Compile, except that if Compile returns error, +// this will panic. +func MustCompile(pattern string, separators ...rune) *Pattern { 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\*\]`. +// QuoteMeta returns a copy of the s having all glob meta characters escaped. func QuoteMeta(s string) string { + // 2 is a pessimistic way of allocating an extra byte per each byte in s. b := make([]byte, 2*len(s)) - - // a byte loop is correct because all meta characters are ASCII j := 0 + // A byte loop is correct here because all meta characters are ASCII. for i := 0; i < len(s); i++ { - if syntax.Special(s[i]) { + if syntax.IsSpecial(s[i]) { b[j] = '\\' j++ } b[j] = s[i] j++ } - return string(b[0:j]) } diff --git a/vendor/github.com/gobwas/glob/internal/debug/debug_disabled.go b/vendor/github.com/gobwas/glob/internal/debug/debug_disabled.go new file mode 100644 index 0000000000..1dcc50d44b --- /dev/null +++ b/vendor/github.com/gobwas/glob/internal/debug/debug_disabled.go @@ -0,0 +1,8 @@ +//go:build !globdebug +// +build !globdebug + +package debug + +const Enabled = false + +func Printf(f string, args ...any) {} diff --git a/vendor/github.com/gobwas/glob/internal/debug/debug_enabled.go b/vendor/github.com/gobwas/glob/internal/debug/debug_enabled.go new file mode 100644 index 0000000000..26aeebd61f --- /dev/null +++ b/vendor/github.com/gobwas/glob/internal/debug/debug_enabled.go @@ -0,0 +1,14 @@ +//go:build globdebug +// +build globdebug + +package debug + +import ( + "fmt" +) + +const Enabled = true + +func Printf(f string, args ...any) { + fmt.Printf(f, args...) +} diff --git a/vendor/github.com/gobwas/glob/internal/debug/tree.go b/vendor/github.com/gobwas/glob/internal/debug/tree.go new file mode 100644 index 0000000000..c716a141ed --- /dev/null +++ b/vendor/github.com/gobwas/glob/internal/debug/tree.go @@ -0,0 +1,8 @@ +package debug + +// Tree renders the matcher tree of a compiled *glob.Pattern. +// +// It is set by package glob at init time: the pattern internals are +// unexported, and this keeps them so while letting the in-module tooling +// (cmd/globtest -v) print them. The format is not stable. +var Tree func(pattern any) string diff --git a/vendor/github.com/gobwas/glob/match.go b/vendor/github.com/gobwas/glob/match.go new file mode 100644 index 0000000000..2b84257062 --- /dev/null +++ b/vendor/github.com/gobwas/glob/match.go @@ -0,0 +1,788 @@ +package glob + +import ( + "fmt" + "slices" + "strconv" + "strings" + "sync" + "unicode/utf8" + "unsafe" + + "github.com/gobwas/glob/internal/debug" +) + +// Match reports whether s matches the pattern. +func (p *Pattern) Match(s string) bool { + var x matchContext + if p.state { + if len(s) < p.minLen || !strings.HasSuffix(s, p.suffix) { + return false + } + state := acquireState() + defer releaseState(state) + x.state = state + } + for { + n, match := p.m.Match(x, s[x.offset:]) + // Note: debug.Enabled is a build-tag constant; when it is false the + // whole block (including the argument evaluation) is compiled away. + if debug.Enabled && x.state != nil { + debug.Printf("stack: %s\n", formatStack(x.state.stack)) + debug.Printf("stars: %s\n", formatStack(x.state.stars)) + } + if match && n == len(s[x.offset:]) { + if debug.Enabled { + debug.Printf("match!\n") + } + return true + } + if x.state == nil { + // The pattern never saves checkpoints (see [needsState]): + // nothing to backtrack to. + return false + } + var ( + c checkpoint + k checkpointKind + ) + switch { + case len(x.state.stars) > 0: + if debug.Enabled { + debug.Printf("has star\n") + } + c = popLast(&x.state.stars) + k = checkpointStars + + case len(x.state.stack) > 0: + if debug.Enabled { + debug.Printf("has stack\n") + } + c = popLast(&x.state.stack) + k = checkpointStack + + default: + if debug.Enabled { + debug.Printf("no match\n") + } + return false + } + x.offset = c.offset + x.kind = k + x.frame = frame{ + path: c.path, + depth: 0, + } + } +} + +// matcher is a node of the tree a pattern compiles into. +// +// Match matches the beginning of s and reports how many bytes it consumed; +// the caller goes on with the rest. A matcher may consume nothing and still +// match: a void does, and so does a non-terminal star -- it stores its +// restart points instead and lets the walk continue, to be resumed from one +// of them on a mismatch later; see [Pattern.Match]. The context tells where +// in the input and in the tree the matcher is; see [matchContext]. +// +// String renders the node for the debug output and cmd/globtest -v; the +// notation is described at [Pattern]. +type matcher interface { + Match(matchContext, string) (n int, matched bool) + String() string +} + +// frame tells where in the matcher tree the walk currently is: the path from +// the root down to the current node, and the depth of the node. +// +// A checkpoint stores the frame's path; resuming it re-enters the tree from +// the root and follows the path back to the very node that saved it (an +// alternative to try, or a star to restart) -- see [Pattern.Match], +// [multiMatcher.Match] and [altMatcher.Match]. +// +// For `{a*b,c}y`, compiled into [{["a"·*·"b"]|"c"}·"y"], the frame at each +// node the walk visits is: +// +// node path depth +// [{["a"·*·"b"]|"c"}·"y"] [] 0 the root +// {["a"·*·"b"]|"c"} [0]* 1 +// ["a"·*·"b"] [0 0]* 2 +// "a" [0 0 0]* 3 +// * [0 0 1] 3 +// "b" [0 0 2] 3 +// "c" [0 1] 2 reached only by resuming the +// checkpoint the alt saved +// "y" [1] 1 +// +// * the zeros are virtual: turning to child #0 records nothing, so the +// path actually stored is [] -- see path below. +type frame struct { + // path addresses the current node: path[d] is the index of the child taken + // at depth d, that is, in the multiMatcher or altMatcher d levels below + // the root. + // + // For `{a*b,c}y`, compiled into [{["a"·*·"b"]|"c"}·"y"]: + // + // [0 1] the "c" alternative -- what the alt checkpoints when it + // enters ["a"·*·"b"], to be tried on a mismatch + // + // [0 0 1] the star: child #1 of ["a"·*·"b"], which is alternative #0 + // of the alt, which is child #0 of the root sequence -- what + // the star's restart points carry + // + // It is recorded lazily: an index is stored only when the walk turns to a + // child other than #0 (see [matchContext.branch]), so the path may be + // shorter than the current depth -- the missing trailing entries are + // implicitly zero, and [frame.index] reads them as such. In the example + // above, at "a" the path is still empty, not [0 0 0]: the alt, alternative + // #0 and "a" were all entered as child #0. + path []int + + // depth is the depth of the current node (the one [frame.path] leads to): + // 0 at the root, 1 at its children, and so on; [matchContext.next] + // increments it on every descent. It is also where the node's own entry in + // path lives: path[depth] is the child to take next -- [frame.index] reads + // it, [matchContext.branch] writes it. + // + // It is not len(path): the path is recorded lazily, so it may fall short + // of the depth, and, when resuming a checkpoint, it is the whole path of + // the checkpoint, reaching beyond the depth all the way down to the node + // to resume at. + // + // To say it the other way, depth is what len(path) would be were the path + // always recorded in full -- the virtual zeros included -- and cut at the + // current node: the length of the node's full address in the tree. + // + // For `{a*b,c}y`, compiled into [{["a"·*·"b"]|"c"}·"y"], the nodes at + // each depth are: + // + // 0 [{["a"·*·"b"]|"c"}·"y"] the root sequence + // 1 {["a"·*·"b"]|"c"}, "y" its children + // 2 ["a"·*·"b"], "c" the alternatives + // 3 "a", *, "b" the children of ["a"·*·"b"] + depth int +} + +// index returns the index of the child to take at the current node: the one +// the path leads to when resuming a checkpoint, #0 otherwise. +func (v frame) index() int { + if v.depth >= len(v.path) { + return 0 + } + return v.path[v.depth] +} + +// checkpoint is a place to resume the walk from on a mismatch. +type checkpoint struct { + // offset is the position in the input to resume at. + offset int + + // path leads to the node that saved the checkpoint; see [frame]. + // + // Unlike a live frame's, it is always at full length -- the virtual zeros + // written out -- so len(path) is the depth of that node, and a checkpoint + // needs no depth of its own: resuming starts at the root and tells it has + // arrived by comparing the walk's depth against len(path); see + // [matchContext.branch], [matchContext.storeStar] and [altMatcher.Match]. + path []int +} + +// checkpointKind tells which pile a checkpoint was taken from during the +// backtracking in [Pattern.Match]. +type checkpointKind int + +const ( + // checkpointStack is an alternative checkpoint saved by altMatcher. + checkpointStack checkpointKind = iota + // checkpointStars is a star restart point saved by starMatcher. + checkpointStars +) + +// matchContext is what a matcher is called with: where in the input and in +// the tree it is, plus the backtracking state shared by the whole walk. It +// is passed by value, so a matcher's changes to it are seen by its +// descendants only. +type matchContext struct { + // offset is the position in the whole input the current node matches from. + // The matchers see only the remainder of the input, so it is what a + // checkpoint records to resume at the same place; see [checkpoint]. + offset int + + // frame is where in the matcher tree the current node is; see [frame]. + frame frame + + // state holds the checkpoint piles and the path arena shared by the + // whole walk. + state *matchState + + // kind tells which pile the checkpoint being resumed was taken from. + // See [altMatcher.Match] for its use. + kind checkpointKind + + // starsFloor is the number of star restart points that existed when + // the walk entered the current alternative. The entries below it were + // born outside of the alternative and must not be discarded by the + // stars inside it; see [matchContext.storeStar]. + starsFloor int +} + +// push saves an alternative checkpoint at the current offset for the node f +// leads to; see [altMatcher.Match]. +func (x matchContext) push(f frame) { + x.state.stack = append(x.state.stack, checkpoint{ + offset: x.offset, + path: f.path, + }) + if debug.Enabled { + debug.Printf( + "checkpoint offset=%d path=%v\n", + x.offset, f.path, + ) + } +} + +// storeStar saves a restart point for the current star at offset bytes +// further in the input; reset tells whether the star may discard the pending +// restart points first, see below. +func (x matchContext) storeStar(offset int, reset bool) { + path := x.frame.path + if d := x.frame.depth; len(path) < d { + // The walk records an index in path only when it turns to a child + // other than the first one; levels entered at child #0 are implicit. + // Store the path at its full length (the missing entries are always + // zeros) so that the alts above can tell this checkpoint from their + // own. See [altMatcher.Match]. + p := x.state.allocPath(d) + copy(p, path) + path = p + } + if reset { + // This star can extend over anything the pending restart points could + // reach -- they are redundant, discard them. See + // research.swtch.com/glob. + // + // However, only the restart points born inside the current alternative + // may be discarded. An outer star, when resumed, re-enters the + // enclosing alt and may pick another alternative -- something this + // star, locked inside its own alternative, can not absorb. See the + // `*{*0,}` test: the outer star must survive the inner one to reach + // the empty alternative. + x.state.stars = x.state.stars[:x.starsFloor] + } + x.state.stars = append(x.state.stars, checkpoint{ + offset: x.offset + offset, + path: path, + }) + if debug.Enabled { + debug.Printf( + "star offset=%d path=%v reset=%t\n", + x.offset+offset, path, reset, + ) + } +} + +// next returns the context for a child of the current node, matching offset +// bytes further in the input: one level deeper in the tree. +func (x matchContext) next(offset int) matchContext { + x.offset = x.offset + offset + x.frame.depth += 1 + return x +} + +// branch returns a copy of the current frame with its path turned to child i +// at the current level, discarding the deeper levels. The new path is +// allocated from the state's arena. +func (x matchContext) branch(i int) frame { + f := x.frame + path := x.state.allocPath(f.depth + 1) + copy(path, f.path) + path[f.depth] = i + f.path = path + return f +} + +// matchState holds the backtracking state of a single [Pattern.Match] call. +// The states are pooled globally: the buffers keep their grown capacity +// between the matches, so a steady-state Match does not allocate them. +type matchState struct { + // stars are the star restart points and stack the alternative + // checkpoints, both LIFO. On a mismatch the walk resumes from the most + // recent restart point, if any, before the most recent alternative; see + // [Pattern.Match]. + stars []checkpoint + stack []checkpoint + + // arena is the buffer the checkpoint paths are allocated from; see + // [matchState.allocPath]. It is bulk-freed when the match ends, which + // spares the per-path lifetime reasoning: a path may be shared between + // the current frame and several checkpoints. + arena []int +} + +// allocPath returns a zeroed []int of length n allocated from the state's +// arena. When the arena runs out of capacity, a fresh chunk is started; the +// paths allocated from the previous chunks stay valid, since the chunks are +// kept alive by the paths referencing them. +func (st *matchState) allocPath(n int) []int { + if cap(st.arena)-len(st.arena) < n { + st.arena = make([]int, 0, max(2*cap(st.arena), n, 32)) + } + p := st.arena[len(st.arena) : len(st.arena)+n : len(st.arena)+n] + st.arena = st.arena[:len(st.arena)+n] + clear(p) + return p +} + +var statePool sync.Pool // Pool[*matchState] + +// acquireState takes a state from the pool, or makes a new one. +func acquireState() *matchState { + if st, _ := statePool.Get().(*matchState); st != nil { + return st + } + return &matchState{} +} + +// releaseState empties the state and puts it back to the pool. +func releaseState(st *matchState) { + resetCheckpoints(&st.stars) + resetCheckpoints(&st.stack) + // The arena holds no references; keep the (largest) chunk as is. + st.arena = st.arena[:0] + statePool.Put(st) +} + +// resetCheckpoints empties s keeping its capacity. The whole backing array +// is zeroed (not only the live part) to drop the references to the +// checkpoint paths popped during the match. +func resetCheckpoints(s *[]checkpoint) { + full := (*s)[:cap(*s)] + clear(full) + *s = full[:0] +} + +// multiMatcher is a sequence, ["a"·*·"b"]: it matches its children one +// after another, each on the input the previous ones left. +type multiMatcher []matcher + +func (ms multiMatcher) String() string { + var sb strings.Builder + sb.WriteByte('[') + for i, m := range ms { + if i > 0 { + sb.WriteString("·") + } + sb.WriteString(m.String()) + } + sb.WriteByte(']') + return sb.String() +} + +func (ms multiMatcher) Match(x matchContext, s string) (n int, ok bool) { + for i := x.frame.index(); i < len(ms); i++ { + if i != x.frame.index() && x.state != nil { + // The path is recorded for the checkpoints the descendants may + // save; in a stateless walk (see [needsState]) there are none + // and nobody would ever read it. + x.frame = x.branch(i) + } + child := x.next(n) + k, ok := ms[i].Match(child, s[n:]) + if debug.Enabled { + debug.Printf( + "[%T@%p] #%d match %#q against %[5]T(%[5]s) at path=%v depth=%d => %d %t\n", + ms, unsafe.SliceData(ms), i, s[n:], ms[i], + child.frame.path, child.frame.depth, k, ok, + ) + } + if !ok { + return 0, false + } + n += k + } + return n, true +} + +// altMatcher is a group of alternatives, {"a"|"b"}: it matches the one the +// walk is at (the first one when entered anew), having saved a checkpoint +// for the next one to be tried on a mismatch later. +type altMatcher []matcher + +func (ms altMatcher) String() string { + var sb strings.Builder + sb.WriteByte('{') + for i, m := range ms { + if i > 0 { + sb.WriteString("|") + } + sb.WriteString(m.String()) + } + sb.WriteByte('}') + return sb.String() +} + +func (ms altMatcher) Match(x matchContext, s string) (int, bool) { + i := x.frame.index() + // Save a checkpoint for the next alternative to consider it in case of + // a mismatch later (if any). This must be done only when: + // + // - the alt is entered for the first time (the resume path ends above + // this level, or there is none); + // + // - the resume path ends exactly at this level with an alternative + // checkpoint -- its job is "try alternative #i", so the one for the + // next alternative must be saved now. Note that a star restart point + // may end at this level too (a star being a direct child of the alt, + // as in `{*,b}`) -- it must not trigger a save. + // + // Otherwise the walk is merely passing through this alt on its way to + // resume a deeper checkpoint -- the one for the next alternative was + // already saved when the alt was entered for the first time, and saving + // it again on every star restart would blow the stack up exponentially. + // See the "alternatives" tests. + if next := i + 1; next < len(ms) { + d := len(x.frame.path) + if x.frame.depth >= d || (x.frame.depth == d-1 && x.kind == checkpointStack) { + x.push(x.branch(next)) + } + } + // The stars below this level may only discard the restart points born + // inside the same alternative; see [matchContext.storeStar]. + x.starsFloor = len(x.state.stars) + child := x.next(0) + n, match := ms[i].Match(child, s) + if debug.Enabled { + debug.Printf( + "[%T@%p] #%d match %#q against %[5]T(%[5]s) at path=%v depth=%d => %d %t\n", + ms, unsafe.SliceData(ms), i, s, ms[i], + child.frame.path, child.frame.depth, n, match, + ) + } + return n, match +} + +// textMatcher is a literal, "abc". +type textMatcher struct { + Text string +} + +func (m *textMatcher) String() string { + return strconv.Quote(m.Text) +} + +func (m *textMatcher) Match(_ matchContext, s string) (int, bool) { + if strings.HasPrefix(s, m.Text) { + return len(m.Text), true + } + return 0, false +} + +// charMatcher is a `?`: any single character but a separator. +type charMatcher struct { + Sep []rune +} + +func (m *charMatcher) String() string { + var sb strings.Builder + sb.WriteByte('?') + if len(m.Sep) > 0 { + sb.WriteByte('(') + formatRunes(&sb, m.Sep) + sb.WriteByte(')') + } + return sb.String() +} + +func (m *charMatcher) Match(_ matchContext, s string) (int, bool) { + if len(s) == 0 { + return 0, false + } + r, n := utf8.DecodeRuneInString(s) + if slices.Contains(m.Sep, r) { + return 0, false + } + return n, true +} + +// starMatcher is a `*` or a `**`: any sequence of characters, but for the +// separators in the former case. +type starMatcher struct { + // Sep are the separators the star may not extend over; empty for `**`. + Sep []rune + // SepStr is Sep as a string, for the byte-wise scans below. + SepStr string + + // Next is the literal the matcher right after this star begins with + // (when it is a textMatcher), set by [annotateStars]. A restart point + // at a position where the literal does not occur is a guaranteed + // mismatch, so the star jumps between its occurrences instead of + // retrying at every rune. + Next string + // Terminal is set by [annotateStars] when nothing follows this star + // anywhere in the pattern: the star then consumes everything in its + // reach at once, and no restart point can change the outcome. + Terminal bool +} + +// reach returns the length of the prefix of s the star may extend over: +// everything up to the nearest separator. +func (m *starMatcher) reach(s string) int { + if m.SepStr == "" { + return len(s) + } + if e := strings.IndexAny(s, m.SepStr); e >= 0 { + return e + } + return len(s) +} + +// storeSkip stores the restart point at the next occurrence of the m.Next +// literal instead of the next rune. +func (m *starMatcher) storeSkip(x matchContext, s string) { + reach := m.reach(s) + // Look for the occurrences starting within the star's reach; the + // literal itself may extend past it (it may contain the separators). + // Note that a valid UTF-8 literal can not match at a mid-rune + // position, so the one-byte skip below is rune-safe. + end := min(reach+len(m.Next), len(s)) + j := strings.Index(s[1:end], m.Next) + if j < 0 || 1+j > reach { + return + } + x.storeStar(1+j, len(m.Sep) == 0) +} + +func (m *starMatcher) String() string { + var sb strings.Builder + sb.WriteByte('*') + if len(m.Sep) > 0 { + sb.WriteByte('(') + formatRunes(&sb, m.Sep) + sb.WriteByte(')') + } + return sb.String() +} + +func (m *starMatcher) Match(x matchContext, s string) (int, bool) { + if m.Terminal { + // Nothing follows this star in the pattern: either it consumes + // the whole remainder within its reach, or the match fails. + return m.reach(s), true + } + if len(s) == 0 { + return 0, true + } + if m.Next != "" { + m.storeSkip(x, s) + return 0, true + } + r, n := utf8.DecodeRuneInString(s) + if !slices.Contains(m.Sep, r) { + // The star may extend over the rune: save the restart point past + // it. A separator-free star (`**`) can extend over anything the + // pending restart points could reach, so it may discard them; see + // [matchContext.storeStar]. + x.storeStar(n, len(m.Sep) == 0) + } + return 0, true +} + +// runeRangeMatcher is a character range class, `[a-z]` or `[!a-z]`. +type runeRangeMatcher struct { + Lo rune + Hi rune + Not bool +} + +func (m *runeRangeMatcher) String() string { + var sb strings.Builder + if m.Not { + sb.WriteByte('!') + } + sb.WriteByte('[') + sb.WriteRune(m.Lo) + sb.WriteByte('-') + sb.WriteRune(m.Hi) + sb.WriteByte(']') + return sb.String() +} + +func (m *runeRangeMatcher) Match(_ matchContext, s string) (int, bool) { + // Note that an invalid byte decodes as U+FFFD, and is matched as such, + // the same way regexp does; only the empty input is a mismatch. + r, n := utf8.DecodeRuneInString(s) + if n == 0 { + return 0, false + } + ok := m.Lo <= r && r <= m.Hi + if ok != m.Not { + return n, true + } + return 0, false +} + +// runeSetMatcher is a character set class, `[abc]` or `[!abc]`. +type runeSetMatcher struct { + Set map[rune]struct{} + Not bool +} + +// formatRunes writes rs, sorted and comma-separated, to sb. +func formatRunes(sb *strings.Builder, rs []rune) { + rs = slices.Clone(rs) // Not to reorder the caller's, e.g. a matcher's Sep. + slices.Sort(rs) + for i, r := range rs { + if i > 0 { + sb.WriteByte(',') + } + sb.WriteRune(r) + } +} + +func (m *runeSetMatcher) String() string { + rs := make([]rune, 0, len(m.Set)) + for r := range m.Set { + rs = append(rs, r) + } + var sb strings.Builder + if m.Not { + sb.WriteByte('!') + } + sb.WriteByte('[') + formatRunes(&sb, rs) + sb.WriteByte(']') + return sb.String() +} + +func (m *runeSetMatcher) Match(_ matchContext, s string) (int, bool) { + // See the note in runeRangeMatcher.Match. + r, n := utf8.DecodeRuneInString(s) + if n == 0 { + return 0, false + } + if _, has := m.Set[r]; has != m.Not { + return n, true + } + return 0, false +} + +// voidMatcher matches the empty string: an empty alternative, `{a,}`. In a +// sequence it is dropped by [normalizeSequence]. +type voidMatcher struct{} + +func (*voidMatcher) String() string { + return "void" +} + +func (*voidMatcher) Match(matchContext, string) (int, bool) { + return 0, true +} + +// The shaped matchers below are the compile-time rewrites of the common +// terminal sub-sequences; see [specialize]. Nothing may follow them in the +// pattern, so each one either consumes the whole remainder of the input or +// fails -- deterministically, storing no checkpoints. + +// prefixMatcher is a terminal `abc*`. +type prefixMatcher struct { + Text string + Sep string +} + +func (m *prefixMatcher) String() string { + return "prefix(" + strconv.Quote(m.Text) + ")" +} + +func (m *prefixMatcher) Match(_ matchContext, s string) (int, bool) { + if strings.HasPrefix(s, m.Text) && noSep(s[len(m.Text):], m.Sep) { + return len(s), true + } + return 0, false +} + +// suffixMatcher is a terminal `*abc`. +type suffixMatcher struct { + Text string + Sep string +} + +func (m *suffixMatcher) String() string { + return "suffix(" + strconv.Quote(m.Text) + ")" +} + +func (m *suffixMatcher) Match(_ matchContext, s string) (int, bool) { + if strings.HasSuffix(s, m.Text) && noSep(s[:len(s)-len(m.Text)], m.Sep) { + return len(s), true + } + return 0, false +} + +// prefixSuffixMatcher is a terminal `abc*def`. +type prefixSuffixMatcher struct { + Prefix string + Suffix string + Sep string +} + +func (m *prefixSuffixMatcher) String() string { + return "prefix_suffix(" + strconv.Quote(m.Prefix) + "," + strconv.Quote(m.Suffix) + ")" +} + +func (m *prefixSuffixMatcher) Match(_ matchContext, s string) (int, bool) { + // The length check keeps the prefix and the suffix from overlapping: + // `a*ant` must not match `ant`. + if len(s) >= len(m.Prefix)+len(m.Suffix) && + strings.HasPrefix(s, m.Prefix) && + strings.HasSuffix(s, m.Suffix) && + noSep(s[len(m.Prefix):len(s)-len(m.Suffix)], m.Sep) { + return len(s), true + } + return 0, false +} + +// containsMatcher is a terminal `*abc*` with the separator-free stars. +type containsMatcher struct { + Text string +} + +func (m *containsMatcher) String() string { + return "contains(" + strconv.Quote(m.Text) + ")" +} + +func (m *containsMatcher) Match(_ matchContext, s string) (int, bool) { + if strings.Contains(s, m.Text) { + return len(s), true + } + return 0, false +} + +// noSep reports whether s contains none of the separators. +func noSep(s, sep string) bool { + return sep == "" || !strings.ContainsAny(s, sep) +} + +// formatCheckpoint renders c as path@offset for the debug output. +func formatCheckpoint(c checkpoint) string { + return fmt.Sprintf("%v@%d", c.path, c.offset) +} + +// formatStack renders the checkpoint pile s for the debug output, the most +// recent one last. +func formatStack(s []checkpoint) string { + var sb strings.Builder + for i, c := range s { + if i > 0 { + sb.WriteString(" -> ") + } + sb.WriteString(formatCheckpoint(c)) + } + return sb.String() +} + +// popLast panics if s is empty. +func popLast[T any, E ~[]T](s *E) T { + n := len(*s) + r := (*s)[n-1] + *s = (*s)[:n-1] + return r +} diff --git a/vendor/github.com/gobwas/glob/match/any.go b/vendor/github.com/gobwas/glob/match/any.go deleted file mode 100644 index 514a9a5c45..0000000000 --- a/vendor/github.com/gobwas/glob/match/any.go +++ /dev/null @@ -1,45 +0,0 @@ -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)) -} diff --git a/vendor/github.com/gobwas/glob/match/any_of.go b/vendor/github.com/gobwas/glob/match/any_of.go deleted file mode 100644 index 8e65356cdc..0000000000 --- a/vendor/github.com/gobwas/glob/match/any_of.go +++ /dev/null @@ -1,82 +0,0 @@ -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) -} diff --git a/vendor/github.com/gobwas/glob/match/btree.go b/vendor/github.com/gobwas/glob/match/btree.go deleted file mode 100644 index a8130e93ea..0000000000 --- a/vendor/github.com/gobwas/glob/match/btree.go +++ /dev/null @@ -1,146 +0,0 @@ -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) -} diff --git a/vendor/github.com/gobwas/glob/match/contains.go b/vendor/github.com/gobwas/glob/match/contains.go deleted file mode 100644 index 0998e95b0e..0000000000 --- a/vendor/github.com/gobwas/glob/match/contains.go +++ /dev/null @@ -1,58 +0,0 @@ -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) -} diff --git a/vendor/github.com/gobwas/glob/match/every_of.go b/vendor/github.com/gobwas/glob/match/every_of.go deleted file mode 100644 index 7c968ee368..0000000000 --- a/vendor/github.com/gobwas/glob/match/every_of.go +++ /dev/null @@ -1,99 +0,0 @@ -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) -} diff --git a/vendor/github.com/gobwas/glob/match/list.go b/vendor/github.com/gobwas/glob/match/list.go deleted file mode 100644 index 7fd763ecd8..0000000000 --- a/vendor/github.com/gobwas/glob/match/list.go +++ /dev/null @@ -1,49 +0,0 @@ -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)) -} diff --git a/vendor/github.com/gobwas/glob/match/match.go b/vendor/github.com/gobwas/glob/match/match.go deleted file mode 100644 index f80e007fb8..0000000000 --- a/vendor/github.com/gobwas/glob/match/match.go +++ /dev/null @@ -1,81 +0,0 @@ -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] - } -} diff --git a/vendor/github.com/gobwas/glob/match/max.go b/vendor/github.com/gobwas/glob/match/max.go deleted file mode 100644 index d72f69efff..0000000000 --- a/vendor/github.com/gobwas/glob/match/max.go +++ /dev/null @@ -1,49 +0,0 @@ -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) -} diff --git a/vendor/github.com/gobwas/glob/match/min.go b/vendor/github.com/gobwas/glob/match/min.go deleted file mode 100644 index db57ac8eb4..0000000000 --- a/vendor/github.com/gobwas/glob/match/min.go +++ /dev/null @@ -1,57 +0,0 @@ -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) -} diff --git a/vendor/github.com/gobwas/glob/match/nothing.go b/vendor/github.com/gobwas/glob/match/nothing.go deleted file mode 100644 index 0d4ecd36b8..0000000000 --- a/vendor/github.com/gobwas/glob/match/nothing.go +++ /dev/null @@ -1,27 +0,0 @@ -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("") -} diff --git a/vendor/github.com/gobwas/glob/match/prefix.go b/vendor/github.com/gobwas/glob/match/prefix.go deleted file mode 100644 index a7347250e8..0000000000 --- a/vendor/github.com/gobwas/glob/match/prefix.go +++ /dev/null @@ -1,50 +0,0 @@ -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) -} diff --git a/vendor/github.com/gobwas/glob/match/prefix_any.go b/vendor/github.com/gobwas/glob/match/prefix_any.go deleted file mode 100644 index 8ee58fe1b3..0000000000 --- a/vendor/github.com/gobwas/glob/match/prefix_any.go +++ /dev/null @@ -1,55 +0,0 @@ -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)) -} diff --git a/vendor/github.com/gobwas/glob/match/prefix_suffix.go b/vendor/github.com/gobwas/glob/match/prefix_suffix.go deleted file mode 100644 index 8208085a19..0000000000 --- a/vendor/github.com/gobwas/glob/match/prefix_suffix.go +++ /dev/null @@ -1,62 +0,0 @@ -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) -} diff --git a/vendor/github.com/gobwas/glob/match/range.go b/vendor/github.com/gobwas/glob/match/range.go deleted file mode 100644 index ce30245a40..0000000000 --- a/vendor/github.com/gobwas/glob/match/range.go +++ /dev/null @@ -1,48 +0,0 @@ -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)) -} diff --git a/vendor/github.com/gobwas/glob/match/row.go b/vendor/github.com/gobwas/glob/match/row.go deleted file mode 100644 index 4379042e42..0000000000 --- a/vendor/github.com/gobwas/glob/match/row.go +++ /dev/null @@ -1,77 +0,0 @@ -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) -} diff --git a/vendor/github.com/gobwas/glob/match/segments.go b/vendor/github.com/gobwas/glob/match/segments.go deleted file mode 100644 index 9ea6f30943..0000000000 --- a/vendor/github.com/gobwas/glob/match/segments.go +++ /dev/null @@ -1,91 +0,0 @@ -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) -} diff --git a/vendor/github.com/gobwas/glob/match/single.go b/vendor/github.com/gobwas/glob/match/single.go deleted file mode 100644 index ee6e3954c1..0000000000 --- a/vendor/github.com/gobwas/glob/match/single.go +++ /dev/null @@ -1,43 +0,0 @@ -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)) -} diff --git a/vendor/github.com/gobwas/glob/match/suffix.go b/vendor/github.com/gobwas/glob/match/suffix.go deleted file mode 100644 index 85bea8c68e..0000000000 --- a/vendor/github.com/gobwas/glob/match/suffix.go +++ /dev/null @@ -1,35 +0,0 @@ -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) -} diff --git a/vendor/github.com/gobwas/glob/match/suffix_any.go b/vendor/github.com/gobwas/glob/match/suffix_any.go deleted file mode 100644 index c5106f8196..0000000000 --- a/vendor/github.com/gobwas/glob/match/suffix_any.go +++ /dev/null @@ -1,43 +0,0 @@ -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) -} diff --git a/vendor/github.com/gobwas/glob/match/super.go b/vendor/github.com/gobwas/glob/match/super.go deleted file mode 100644 index 3875950bb8..0000000000 --- a/vendor/github.com/gobwas/glob/match/super.go +++ /dev/null @@ -1,33 +0,0 @@ -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("") -} diff --git a/vendor/github.com/gobwas/glob/match/text.go b/vendor/github.com/gobwas/glob/match/text.go deleted file mode 100644 index 0a17616d3c..0000000000 --- a/vendor/github.com/gobwas/glob/match/text.go +++ /dev/null @@ -1,45 +0,0 @@ -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) -} diff --git a/vendor/github.com/gobwas/glob/parse.go b/vendor/github.com/gobwas/glob/parse.go new file mode 100644 index 0000000000..30114dfcf6 --- /dev/null +++ b/vendor/github.com/gobwas/glob/parse.go @@ -0,0 +1,679 @@ +package glob + +import ( + "slices" + "unicode/utf8" + + "github.com/gobwas/glob/internal/debug" + "github.com/gobwas/glob/syntax" +) + +// compile parses the pattern into a matcher tree (see below), simplifies and +// specializes it, and computes the match-time hints and preconditions; see +// [simplify], [specialize], [annotateStars], [needsState], [minLength] and +// [requiredSuffix]. It is what [Compile] wraps. +func compile(str string, sep []rune) (*Pattern, error) { + if debug.Enabled { + debug.Printf("compiling %#q\n", str) + } + // The matchers keep sep and read it while matching, and the variadic slice + // may alias an array owned by the caller: give them a copy of their own. + // + // The pattern itself keeps the slice as given, to return it from + // Separators() without cloning. + var ( + sepCopy = slices.Clone(sep) + sepStr = string(sep) + ) + + type operator struct { + kind int + index int + } + const ( + opTerms = iota + opList + ) + /* + Stack-based parsing is a technique used to evaluate mathematical + expressions by leveraging the properties of the LIFO (Last-In, + First-Out) data structure, the stack. It involves using two stacks: one + for operands (numbers) and one for operators. By processing the + expression from left to right and strategically pushing and popping + elements from the stacks, the expression can be effectively evaluated. + + https://cp-algorithms.com/string/expression_parsing.html + + Here the operands are matchers and the only operators are the braces + and the commas inside them, so it goes as follows: + + - a leaf token (text, `?`, `*`, `**`, `[...]`) pushes its matcher + onto the stack; + + - `{` pushes two operators, both remembering the current stack + length: opTerms marks where the alternatives of the group will + be collected, opList marks where the terms of the current + alternative begin; + + - `,` pops the opList, collapses the terms above its index into a + single multiMatcher (or a voidMatcher when there are none, as in + `{,a}`), and pushes a fresh opList for the next alternative; + + - `}` pops the opList and collapses the last alternative the same + way, then pops the opTerms and collapses everything above its + index -- one matcher per alternative by now -- into an + altMatcher; + + - at the EOF whatever is left on the stack is the top-level + sequence; a leftover operator means an unclosed `{`. + + For example, `a{b*,c}d` goes like this (list@i is an opList with + index i, likewise terms@i): + + token stack operators + a "a" + { "a" terms@1 list@1 + b "a" "b" terms@1 list@1 + * "a" "b" * terms@1 list@1 + , "a" ["b"·*] terms@1 list@2 + c "a" ["b"·*] "c" terms@1 list@2 + } "a" ["b"·*] ["c"] terms@1 + "a" {["b"·*]|["c"]} + d "a" {["b"·*]|["c"]} "d" + EOF ["a"·{["b"·*]|["c"]}·"d"] + + The result is then simplified (["c"] becomes "c") and specialized; + see [simplify] and [specialize]. + */ + var ( + stack []matcher + operators []operator + ) + lex := syntax.NewLexer(str) +parsing: + for { + token := lex.Next() + if debug.Enabled { + debug.Printf("token: %s\n", token) + } + switch token.Type { + case syntax.EOF: + break parsing + + case syntax.Error: + return nil, &SyntaxError{ + Offset: lex.Offset(), + Reason: token.Data, + } + + case syntax.Single: + stack = append(stack, &charMatcher{ + Sep: sepCopy, + }) + + case syntax.Text: + stack = append(stack, &textMatcher{ + Text: token.Data, + }) + + case syntax.RangeOpen: + m, err := parseRange(lex) + if err != nil { + return nil, err + } + stack = append(stack, m) + + case syntax.Any: + stack = append(stack, &starMatcher{ + Sep: sepCopy, + SepStr: sepStr, + }) + + case syntax.Super: + stack = append(stack, &starMatcher{ + Sep: nil, + }) + + case syntax.TermsOpen: + // Note that the `{` opens both the group and its first + // alternative: every alternative is delimited by an opList + // operator. This way TermsClose always collapses the trailing + // alternative into a single matcher first, even when the group + // has no commas at all, e.g. `{ab*}`. + operators = append(operators, + operator{kind: opTerms, index: len(stack)}, + operator{kind: opList, index: len(stack)}, + ) + if debug.Enabled { + debug.Printf("terms enter: %d\n", len(stack)) + } + + case syntax.TermSeparator: + k := len(operators) - 1 + if k < 0 { + return nil, &SyntaxError{ + Offset: lex.Offset(), + Reason: "unexpected `,`", + } + } + x := operators[k] + if x.kind == opList { + // Remove the most recent "comma" operator. + // Note that the previous one is the terms operator. + operators = operators[:k] + } + i := x.index + // Handle the `{,a}` case. + if i == len(stack) { + // Empty matchers. + stack = append(stack, &voidMatcher{}) + } else { + stack[i] = multiMatcher(slices.Clone(stack[i:])) + stack = stack[:i+1] + if debug.Enabled { + debug.Printf("terms next: %d: %s\n", i, stack[i]) + } + } + operators = append(operators, operator{ + kind: opList, + index: len(stack), + }) + if debug.Enabled { + debug.Printf("terms separator: %d\n", len(stack)) + } + + case syntax.TermsClose: + for { + k := len(operators) - 1 + if k < 0 { + return nil, &SyntaxError{ + Offset: lex.Offset(), + Reason: "unexpected `}`", + } + } + x := operators[k] + operators = operators[:k] + + i := x.index + c := slices.Clone(stack[i:]) + var m matcher + switch x.kind { + case opTerms: + m = altMatcher(c) + case opList: + m = multiMatcher(c) + } + // Handle the `{a,}` case. + if i == len(stack) { + stack = append(stack, m) + } else { + stack = stack[:i+1] + stack[i] = m + } + + if debug.Enabled { + debug.Printf( + "terms leave(%d): %d: %s\n", + x.kind, i, stack[i], + ) + } + if x.kind == opTerms { + break + } + } + + default: + return nil, &SyntaxError{ + Offset: lex.Offset(), + Reason: "unexpected token " + token.String(), + } + } + } + if len(operators) != 0 { + return nil, &SyntaxError{ + Offset: lex.Offset(), + Reason: "unclosed `{`", + } + } + m := simplify(multiMatcher(stack)) + m = specialize(m, true) + annotateStars(m, true) + if debug.Enabled { + debug.Printf("compiled %#q: %s\n", str, m) + } + p := &Pattern{ + str: str, + sep: sep, + m: m, + state: needsState(m), + } + if p.state { + p.minLen = minLength(m) + p.suffix = requiredSuffix(m) + } + return p, nil +} + +// parseRange parses a character class, called right after its opening `[` +// was read; it consumes the tokens up to and including the closing `]`. The +// class is either a range, `[a-c]`, or a set, `[abc]`, either possibly +// negated with a leading `!`; see [runeRangeMatcher] and [runeSetMatcher]. +func parseRange(lex *syntax.Lexer) (matcher, error) { + // -1 marks a range boundary as unset: any decoded rune, including + // U+0000, is non-negative. + var ( + not bool + lo, hi rune = -1, -1 + chars map[rune]struct{} + ) + for { + token := lex.Next() + switch token.Type { + case syntax.EOF: + return nil, &SyntaxError{ + Offset: lex.Offset(), + Reason: "unclosed `[`", + } + + case syntax.Error: + return nil, &SyntaxError{ + Offset: lex.Offset(), + Reason: token.Data, + } + + case syntax.Not: + not = true + + case syntax.RangeLo: + r, w := utf8.DecodeRuneInString(token.Data) + if len(token.Data) > w { + return nil, &SyntaxError{ + Offset: lex.Offset(), + Reason: "unexpected length of range lo character", + } + } + lo = r + + case syntax.RangeBetween: + // The `-` between lo and hi: nothing to do. + + case syntax.RangeHi: + r, w := utf8.DecodeRuneInString(token.Data) + if len(token.Data) > w { + return nil, &SyntaxError{ + Offset: lex.Offset(), + Reason: "unexpected length of range hi character", + } + } + hi = r + + if hi < lo { + return nil, &SyntaxError{ + Offset: lex.Offset(), + Reason: "range hi character is less than lo", + } + } + + case syntax.Text: + chars = make(map[rune]struct{}) + for _, r := range token.Data { + chars[r] = struct{}{} + } + + case syntax.RangeClose: + isRange := lo >= 0 && hi >= 0 + isChars := chars != nil + + if isChars == isRange { + return nil, &SyntaxError{ + Offset: lex.Offset(), + Reason: "could not parse range", + } + } + if isRange { + return &runeRangeMatcher{ + Lo: lo, + Hi: hi, + Not: not, + }, nil + } + return &runeSetMatcher{ + Set: chars, + Not: not, + }, nil + } + } +} + +// simplify rewrites the freshly parsed tree into its canonical shape, bottom +// up: the sequences are normalized (see [normalizeSequence]), and a sequence +// or a group of alternatives with a single child is replaced by the child, +// with none -- by a void. +func simplify(m matcher) matcher { + var ( + ms []matcher + isMulti bool + ) + switch v := m.(type) { + case multiMatcher: + ms, isMulti = v, true + case altMatcher: + ms = v + default: + return m + } + for i, m := range ms { + ms[i] = simplify(m) + } + if isMulti { + ms = normalizeSequence(ms) + } + switch len(ms) { + case 0: + return &voidMatcher{} + case 1: + return ms[0] + } + if isMulti { + return multiMatcher(ms) + } + return altMatcher(ms) +} + +// normalizeSequence rewrites a sequence of (already simplified) matchers +// into a simpler equivalent one: +// +// ["a"·["b"·"c"]·"d"] => ["a"·"b"·"c"·"d"] inline the nested sequences +// ["a"·void] => ["a"] drop the void matchers +// ["a"·"b"] => ["ab"] merge the adjacent literals +// [*·**] => [**] coalesce the adjacent stars +// +// Longer literals also make better star jumps; see [annotateStars]. +func normalizeSequence(ms []matcher) []matcher { + if !needsNormalize(ms) { + // The common case: nothing to rewrite, no copy needed. + return ms + } + out := make([]matcher, 0, len(ms)) + var push func(m matcher) + push = func(m matcher) { + switch v := m.(type) { + case multiMatcher: + for _, c := range v { + push(c) + } + return + case *voidMatcher: + return + case *textMatcher: + if len(out) > 0 { + if prev, ok := out[len(out)-1].(*textMatcher); ok { + out[len(out)-1] = &textMatcher{Text: prev.Text + v.Text} + return + } + } + case *starMatcher: + if len(out) > 0 { + if prev, ok := out[len(out)-1].(*starMatcher); ok { + // Adjacent stars are equivalent to the most general + // of them: the one not limited by separators, if any. + if len(prev.Sep) > 0 && len(v.Sep) == 0 { + out[len(out)-1] = v + } + return + } + } + } + out = append(out, m) + } + for _, m := range ms { + push(m) + } + return out +} + +// needsNormalize reports whether [normalizeSequence] would change ms, so +// that the common case skips the copy. +func needsNormalize(ms []matcher) bool { + for i, m := range ms { + switch m.(type) { + case multiMatcher, *voidMatcher: + return true + case *textMatcher: + if i > 0 { + if _, ok := ms[i-1].(*textMatcher); ok { + return true + } + } + case *starMatcher: + if i > 0 { + if _, ok := ms[i-1].(*starMatcher); ok { + return true + } + } + } + } + return false +} + +// specialize rewrites the terminal sub-sequences of the simplified matcher +// tree into the shaped matchers -- [prefixMatcher], [suffixMatcher], +// [prefixSuffixMatcher] and [containsMatcher]; see [foldTail] for the +// rewrites. The tail flag tells whether nothing follows m in the pattern; +// only there the rewrites apply, since a shaped matcher consumes the whole +// remainder of the input. +func specialize(m matcher, tail bool) matcher { + switch v := m.(type) { + case altMatcher: + // Every alternative ends where the alt ends. + for i, c := range v { + v[i] = specialize(c, tail) + } + return v + + case multiMatcher: + for i, c := range v { + v[i] = specialize(c, tail && i == len(v)-1) + } + if !tail { + return v + } + ms := foldTail([]matcher(v)) + if len(ms) == 1 { + return ms[0] + } + return multiMatcher(ms) + } + return m +} + +// foldTail repeatedly folds the two trailing matchers of the terminal +// sequence ms into a shaped one, while possible: +// +// [..·"abc"·*] => [..·prefix("abc")] +// [..·*·"abc"] => [..·suffix("abc")] +// [..·"abc"·prefix("def")] => [..·prefix("abcdef")] +// [..·*·prefix("abc")] => [..·contains("abc")] (separator-free) +// [..·"abc"·suffix("def")] => [..·prefix_suffix("abc","def")] +// [..·*·contains("abc")] => [..·contains("abc")] (separator-free) +func foldTail(ms []matcher) []matcher { + for len(ms) >= 2 { + var ( + prev = ms[len(ms)-2] + folded matcher + ) + switch last := ms[len(ms)-1].(type) { + case *starMatcher: + if t, ok := prev.(*textMatcher); ok { + folded = &prefixMatcher{Text: t.Text, Sep: last.SepStr} + } + + case *textMatcher: + if star, ok := prev.(*starMatcher); ok { + folded = &suffixMatcher{Text: last.Text, Sep: star.SepStr} + } + + case *prefixMatcher: + switch p := prev.(type) { + case *textMatcher: + folded = &prefixMatcher{Text: p.Text + last.Text, Sep: last.Sep} + case *starMatcher: + if p.SepStr == "" && last.Sep == "" { + folded = &containsMatcher{Text: last.Text} + } + } + + case *suffixMatcher: + if t, ok := prev.(*textMatcher); ok { + folded = &prefixSuffixMatcher{ + Prefix: t.Text, + Suffix: last.Text, + Sep: last.Sep, + } + } + + case *containsMatcher: + if star, ok := prev.(*starMatcher); ok && star.SepStr == "" { + folded = last + } + } + if folded == nil { + break + } + ms = ms[:len(ms)-1] + ms[len(ms)-1] = folded + } + return ms +} + +// annotateStars computes the compile-time hints for the star matchers, in +// order to keep the number of restart points they store at match time low: +// +// - a star directly followed by a literal jumps between the literal +// occurrences instead of retrying at every rune (see +// [starMatcher.storeSkip]); +// +// - a star with nothing after it anywhere in the pattern (tail is true +// for m and the star closes it) consumes its whole reach at once and +// stores no restart points at all. +func annotateStars(m matcher, tail bool) { + switch v := m.(type) { + case multiMatcher: + for i, c := range v { + last := i == len(v)-1 + star, ok := c.(*starMatcher) + if !ok { + annotateStars(c, tail && last) + continue + } + star.Terminal = tail && last + if !last { + star.Next = leadingLiteral(v[i+1]) + } + } + case altMatcher: + for _, c := range v { + annotateStars(c, tail) + } + case *starMatcher: + v.Terminal = tail + } +} + +// leadingLiteral returns the literal the given matcher is guaranteed to +// begin its match with, if any. +func leadingLiteral(m matcher) string { + switch v := m.(type) { + case *textMatcher: + return v.Text + case *prefixMatcher: + return v.Text + case *prefixSuffixMatcher: + return v.Prefix + } + return "" +} + +// minLength returns the minimum length in bytes of a string m can match. +func minLength(m matcher) (n int) { + switch v := m.(type) { + case *textMatcher: + return len(v.Text) + case *charMatcher, *runeRangeMatcher, *runeSetMatcher: + return 1 + case *prefixMatcher: + return len(v.Text) + case *suffixMatcher: + return len(v.Text) + case *prefixSuffixMatcher: + return len(v.Prefix) + len(v.Suffix) + case *containsMatcher: + return len(v.Text) + case multiMatcher: + for _, c := range v { + n += minLength(c) + } + return n + case altMatcher: + n = minLength(v[0]) + for _, c := range v[1:] { + n = min(n, minLength(c)) + } + return n + } + return 0 // A star or a void. +} + +// requiredSuffix returns the literal every string m matches must end with. +func requiredSuffix(m matcher) string { + switch v := m.(type) { + case *textMatcher: + return v.Text + case *suffixMatcher: + return v.Text + case *prefixSuffixMatcher: + return v.Suffix + case multiMatcher: + return requiredSuffix(v[len(v)-1]) + case altMatcher: + s := requiredSuffix(v[0]) + for _, c := range v[1:] { + s = commonSuffix(s, requiredSuffix(c)) + if s == "" { + break + } + } + return s + } + return "" // A star, a single-character matcher or a void. +} + +// commonSuffix returns the longest common suffix of a and b, never splitting +// a multi-byte rune. +func commonSuffix(a, b string) string { + i := 0 + for i < len(a) && i < len(b) { + ra, wa := utf8.DecodeLastRuneInString(a[:len(a)-i]) + rb, wb := utf8.DecodeLastRuneInString(b[:len(b)-i]) + if ra != rb || wa != wb { + break + } + i += wa + } + return a[len(a)-i:] +} + +// needsState reports whether matching m may save a checkpoint. Only the +// alts and the non-terminal stars do; a pattern without them is matched +// with a plain call chain -- see [Pattern.Match]. +func needsState(m matcher) bool { + switch v := m.(type) { + case altMatcher: + return true + case multiMatcher: + return slices.ContainsFunc(v, needsState) + case *starMatcher: + return !v.Terminal + } + return false +} diff --git a/vendor/github.com/gobwas/glob/readme.md b/vendor/github.com/gobwas/glob/readme.md index f58144e733..9f692571f1 100644 --- a/vendor/github.com/gobwas/glob/readme.md +++ b/vendor/github.com/gobwas/glob/readme.md @@ -1,6 +1,6 @@ # glob.[go](https://golang.org) -[![GoDoc][godoc-image]][godoc-url] [![Build Status][travis-image]][travis-url] +[![GoDoc][godoc-image]][godoc-url] [![CI][ci-image]][ci-url] > Go Globbing Library. @@ -19,130 +19,225 @@ package main import "github.com/gobwas/glob" func main() { - var g glob.Glob - + var g *glob.Pattern + // create simple glob g = glob.MustCompile("*.github.com") g.Match("api.github.com") // true - - // quote meta characters and then create simple glob + + // 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.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 + + // 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.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 + + // 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.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 + g.Match("at") // false + g.Match("zat") // false } ``` +`Compile` reports malformed patterns with a `*glob.SyntaxError` carrying the +byte offset and the reason: + +```go +_, err := glob.Compile("{a,b") +// err: glob: syntax error at 4: unclosed `{` +``` + +A compiled `Pattern` captures what it was compiled from, so it can be passed +around instead of the raw arguments and inspected when needed (`String()` makes +it a `fmt.Stringer`, like `regexp.Regexp`): + +```go +g := glob.MustCompile("*.github.com", '.') +g.String() // "*.github.com" +g.Separators() // []rune{'.'} +``` + +## Syntax + +Syntax is inspired by [standard wildcards](http://tldp.org/LDP/GNU-Linux-Tools-Summary/html/x11655.htm), +with one addition: `**` (the "super-asterisk"), which matches any sequence +of characters *including* the separators, where `*` stops at them. Note that +it is just that -- a `*` that crosses separators -- and not the `**/` +"globstar" of shells and file globbers: `**/x` requires the literal `/`, so +it does not match `x`; use `{**/,}x` for that. The same applies to a +`**` between separators, e.g. `a/**/b` does not match `a/b`. + +``` +pattern: + { term } + +term: + `*` matches any sequence of non-separator characters + `**` matches any sequence of characters + `?` matches any single non-separator character + `[` [ `!` ] class `]` + character class; `!` negates it + `{` pattern-list `}` + pattern alternatives + c matches character c (c != `*`, `**`, `?`, `\`, `[`, `{`, `}`) + `\` c matches character c + +class: + lo `-` hi matches character c for lo <= c <= hi + { c } matches any of the listed characters (c != `\`, `]`; + `\` c matches c, `-` is literal here); must be non-empty + +pattern-list: + pattern { `,` pattern } + comma-separated (without spaces) patterns +``` + +### Escaping + +The backslash is the escape character: `\*` is a literal asterisk, and a +backslash itself is `\\`. Mind the Go string literals: `"foo\\bar"` is the +pattern `foo\bar`, which is the literal `foobar`, not `foo\bar`. To match a +backslash (e.g. in the Windows paths) write `"foo\\\\bar"` or `` `foo\\bar` ``, +or use `QuoteMeta` on the literal part. + +### Separators + +The separators are not part of the pattern syntax -- they are configured +once, at compilation time, as the extra arguments of `Compile`: + +```go +g := glob.MustCompile("api.*.com", '.', '/') +``` + +They only limit the wildcards: `*` and `?` never match a separator, while +`**` matches across them; the literals and the character classes are not +affected. With no separators given, `*` and `**` are equivalent. A compiled +`*glob.Pattern` keeps its separators for all matches -- to match the same +pattern with different separators, compile it again. + ## 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. +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. +If you will not use compiled `*glob.Pattern` 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: +`Match` performs zero allocations and is safe for concurrent use. Common +pattern shapes (literals, prefixes, suffixes, substrings) are recognized at +compile time and matched with plain string comparisons; the backtracking +engine behind the rest is differentially fuzzed against the `regexp` package +(see `FuzzMatchRegexp`). -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: +Run `go test -bench=.` from source root to see the benchmarks (the numbers +below are from an Apple M4): 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. \ No newline at end of file +`[a-z][!a-x]*cat*[h][!b]*eyes*` | `my cat has very bright eyes` | `true` | 141 +`[a-z][!a-x]*cat*[h][!b]*eyes*` | `my dog has very bright eyes` | `false` | 46 +`https://*.google.*` | `https://account.google.com` | `true` | 16 +`https://*.google.*` | `https://google.com` | `false` | 13 +`{https://*.google.*,*yandex.*,*yahoo.*,*mail.ru}` | `http://yahoo.com` | `true` | 61 +`{https://*.google.*,*yandex.*,*yahoo.*,*mail.ru}` | `http://google.com` | `false` | 70 +`{https://*gobwas.com,http://exclude.gobwas.com}` | `https://safe.gobwas.com` | `true` | 24 +`{https://*gobwas.com,http://exclude.gobwas.com}` | `http://safe.gobwas.com` | `false` | 32 +`google.com` | `google.com` | `true` | 5.0 +`google.com` | `gobwas.com` | `false` | 3.9 +`abc*` | `abcdef` | `true` | 4.1 +`abc*` | `af` | `false` | 3.0 +`*def` | `abcdef` | `true` | 4.1 +`*def` | `af` | `false` | 2.9 +`ab*ef` | `abcdef` | `true` | 6.0 +`ab*ef` | `af` | `false` | 3.0 + +The same things with the `regexp` package -- not to pick on it (it is a +general-purpose engine with much stronger guarantees), but as a reference +for how the glob-shaped specialization pays off per pattern. The regular +expressions are the exact equivalents: anchored, and with the `s` flag +where there is a `*`, since a `*` matches a newline like any other +character (see `BenchmarkCompareGlobAndRegexp`): + +Pattern | Fixture | Match | Speed (ns/op) | glob is +--------|---------|-------|---------------|-------- +`(?s)^[a-z][^a-x].*cat.*[h][^b].*eyes.*$` | `my cat has very bright eyes` | `true` | 505 | 3.6x faster +`(?s)^[a-z][^a-x].*cat.*[h][^b].*eyes.*$` | `my dog has very bright eyes` | `false` | 221 | 4.9x faster +`(?s)^https://.*\.google\..*$` | `https://account.google.com` | `true` | 251 | 16x faster +`(?s)^https://.*\.google\..*$` | `https://google.com` | `false` | 128 | 9.6x faster +`(?s)^(https://.*\.google\..*\|.*yandex\..*\|.*yahoo\..*\|.*mail\.ru)$` | `http://yahoo.com` | `true` | 396 | 6.5x faster +`(?s)^(https://.*\.google\..*\|.*yandex\..*\|.*yahoo\..*\|.*mail\.ru)$` | `http://google.com` | `false` | 558 | 8.0x faster +`(?s)^(https://.*gobwas\.com\|http://exclude\.gobwas\.com)$` | `https://safe.gobwas.com` | `true` | 210 | 8.8x faster +`(?s)^(https://.*gobwas\.com\|http://exclude\.gobwas\.com)$` | `http://safe.gobwas.com` | `false` | 46 | 1.4x faster +`^google\.com$` | `google.com` | `true` | 25 | 5.0x faster +`^google\.com$` | `gobwas.com` | `false` | 17 | 4.3x faster +`(?s)^abc.*$` | `abcdef` | `true` | 43 | 10x faster +`(?s)^abc.*$` | `af` | `false` | 1.5 | 2.0x slower +`(?s)^.*def$` | `abcdef` | `true` | 73 | 18x faster +`(?s)^.*def$` | `af` | `false` | 1.5 | 1.9x slower +`(?s)^ab.*ef$` | `abcdef` | `true` | 77 | 13x faster +`(?s)^ab.*ef$` | `af` | `false` | 1.5 | 2.0x slower + +(The three `slower` rows are the tiny-mismatch cases. Both engines reject +them with the same literal check; `regexp` just reaches it through less +call overhead. In absolute terms it is 1.5ns vs 3ns -- negligible either +way.) + +[godoc-image]: https://pkg.go.dev/badge/github.com/gobwas/glob.svg +[godoc-url]: https://pkg.go.dev/github.com/gobwas/glob +[ci-image]: https://github.com/gobwas/glob/actions/workflows/ci.yml/badge.svg?branch=master +[ci-url]: https://github.com/gobwas/glob/actions/workflows/ci.yml diff --git a/vendor/github.com/gobwas/glob/syntax/ast/ast.go b/vendor/github.com/gobwas/glob/syntax/ast/ast.go deleted file mode 100644 index 3220a694a9..0000000000 --- a/vendor/github.com/gobwas/glob/syntax/ast/ast.go +++ /dev/null @@ -1,122 +0,0 @@ -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 "" - } -} diff --git a/vendor/github.com/gobwas/glob/syntax/ast/parser.go b/vendor/github.com/gobwas/glob/syntax/ast/parser.go deleted file mode 100644 index 429b409430..0000000000 --- a/vendor/github.com/gobwas/glob/syntax/ast/parser.go +++ /dev/null @@ -1,157 +0,0 @@ -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 - } - } -} diff --git a/vendor/github.com/gobwas/glob/syntax/lexer.go b/vendor/github.com/gobwas/glob/syntax/lexer.go new file mode 100644 index 0000000000..4286080237 --- /dev/null +++ b/vendor/github.com/gobwas/glob/syntax/lexer.go @@ -0,0 +1,381 @@ +// Package syntax implements the lexer of the glob pattern syntax. The parser +// lives in package glob; the syntax itself is described at [glob.Compile]. +package syntax + +import ( + "bytes" + "fmt" + "slices" + "unicode/utf8" +) + +// TokenType tells the kind of a [Token]. +type TokenType int + +const ( + // EOF marks the end of the input; the lexer returns it repeatedly. + EOF TokenType = iota + // Error carries an error message in Token.Data; the lexer keeps + // returning it once it happened. Note that the lexer catches only the + // errors local to a token (an invalid UTF-8 sequence, a malformed + // character class): the structural ones, like an unclosed `{`, are for + // the parser to detect. + Error + // Text is a run of literal characters, with the escapes resolved. + Text + // Any is the `*` wildcard. + Any + // Super is the `**` wildcard. + Super + // Single is the `?` wildcard. + Single + // Not is the `!` right after the `[` of a character class. + Not + // TermSeparator is the `,` between the alternatives of a `{...}` group. + // Outside of a group a comma is a plain Text character. + TermSeparator + // RangeOpen and RangeClose are the `[` and `]` of a character class. + // Between them the lexer produces either a Text token (a set of + // characters, `[abc]`) or a RangeLo, RangeBetween, RangeHi triple (a + // range, `[a-c]`), possibly preceded by Not. + RangeOpen + RangeClose + RangeLo + RangeHi + RangeBetween + // TermsOpen and TermsClose are the `{` and `}` of an alternatives group. + TermsOpen + TermsClose +) + +func (tt TokenType) String() string { + switch tt { + case EOF: + return "eof" + case Error: + return "error" + case Text: + return "text" + case Any: + return "any" + case Super: + return "super" + case Single: + return "single" + case Not: + return "not" + case TermSeparator: + 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 "" + } +} + +// Token is a lexeme of the pattern: its kind and the source text it was +// read from (or the error message for Error, the literal characters with +// the escapes resolved for Text). +type Token struct { + Type TokenType + Data string +} + +func (t Token) String() string { + return fmt.Sprintf("%v<%q>", t.Type, t.Data) +} + +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, +} + +// IsSpecial reports whether c is a glob meta character, that is, one that +// [glob.QuoteMeta] escapes. Note that `,`, `!` and `-` are not among them: +// they are special only inside `{...}` and `[...]` respectively, which are. +func IsSpecial(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 +} + +// eof is the end-of-input sentinel. It must not collide with any rune that +// can appear in a valid pattern -- note that U+0000 can. +const eof rune = -1 + +// Lexer splits a pattern into tokens; see [Lexer.Next]. +type Lexer struct { + data string + pos int + err error + + tokens tokens + termsLevel int + + lastRune rune + lastRuneSize int + hasRune bool +} + +// NewLexer returns a lexer over the source pattern. +func NewLexer(source string) *Lexer { + l := &Lexer{ + data: source, + tokens: tokens(make([]Token, 0, 4)), + } + return l +} + +// Offset returns the byte offset in the source the lexer stopped at, that +// is, the position right after the most recently returned token. +func (l *Lexer) Offset() int { + return l.pos +} + +// Next returns the next token. Once the input is over it returns EOF, and +// once an error happened it returns that Error, repeatedly. +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 && w == 1 { + // An invalid encoding: a valid U+FFFD decodes at its width of 3. + l.errorf("invalid UTF-8 sequence") + 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 ...any) { + 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{TermSeparator, 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 { + if escaped { + l.errorf("trailing backslash") + } + break + } + + if !escaped { + if r == char_escape { + escaped = true + continue + } + if slices.Index(breakers, r) != -1 { + l.unread() + break reading + } + } + + escaped = false + data = append(data, r) + } + + if len(data) > 0 { + l.tokens.push(Token{Text, string(data)}) + } +} diff --git a/vendor/github.com/gobwas/glob/syntax/lexer/lexer.go b/vendor/github.com/gobwas/glob/syntax/lexer/lexer.go deleted file mode 100644 index a1c8d1962a..0000000000 --- a/vendor/github.com/gobwas/glob/syntax/lexer/lexer.go +++ /dev/null @@ -1,273 +0,0 @@ -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)}) - } -} diff --git a/vendor/github.com/gobwas/glob/syntax/lexer/token.go b/vendor/github.com/gobwas/glob/syntax/lexer/token.go deleted file mode 100644 index 2797c4e83a..0000000000 --- a/vendor/github.com/gobwas/glob/syntax/lexer/token.go +++ /dev/null @@ -1,88 +0,0 @@ -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) -} diff --git a/vendor/github.com/gobwas/glob/syntax/syntax.go b/vendor/github.com/gobwas/glob/syntax/syntax.go deleted file mode 100644 index 1d168b1482..0000000000 --- a/vendor/github.com/gobwas/glob/syntax/syntax.go +++ /dev/null @@ -1,14 +0,0 @@ -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) -} diff --git a/vendor/github.com/gobwas/glob/util/runes/runes.go b/vendor/github.com/gobwas/glob/util/runes/runes.go deleted file mode 100644 index a723556410..0000000000 --- a/vendor/github.com/gobwas/glob/util/runes/runes.go +++ /dev/null @@ -1,154 +0,0 @@ -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) -} diff --git a/vendor/github.com/gobwas/glob/util/strings/strings.go b/vendor/github.com/gobwas/glob/util/strings/strings.go deleted file mode 100644 index e8ee1920b1..0000000000 --- a/vendor/github.com/gobwas/glob/util/strings/strings.go +++ /dev/null @@ -1,39 +0,0 @@ -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 -} diff --git a/vendor/github.com/klauspost/compress/README.md b/vendor/github.com/klauspost/compress/README.md index 0e9f170d01..bf31b905f8 100644 --- a/vendor/github.com/klauspost/compress/README.md +++ b/vendor/github.com/klauspost/compress/README.md @@ -6,7 +6,9 @@ This package provides various compression algorithms. * [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. +* [lzw](https://github.com/klauspost/compress/tree/master/lzw) is a drop-in replacement for `compress/lzw` with 1.4-4x faster decompression and 1.1-2.7x faster compression, depending on the data. * [huff0](https://github.com/klauspost/compress/tree/master/huff0) and [FSE](https://github.com/klauspost/compress/tree/master/fse) implementations for raw entropy encoding. +* [Xpress](https://github.com/klauspost/compress/tree/master/xpress) decompression of the Microsoft XPRESS (MS-XCA) plain LZ77 and LZ77+Huffman formats (the LZ77+Huffman variant is the one used in WIM images and Windows Compact OS / WOF data). * [gzhttp](https://github.com/klauspost/compress/tree/master/gzhttp) Provides client and server wrappers for handling gzipped/zstd HTTP requests efficiently. * [pgzip](https://github.com/klauspost/pgzip) is a separate package that provides a very fast parallel gzip implementation. diff --git a/vendor/github.com/klauspost/compress/huff0/decompress_amd64.s b/vendor/github.com/klauspost/compress/huff0/decompress_amd64.s index c5d4a710a0..920628c514 100644 --- a/vendor/github.com/klauspost/compress/huff0/decompress_amd64.s +++ b/vendor/github.com/klauspost/compress/huff0/decompress_amd64.s @@ -47,12 +47,12 @@ skip_fill0: SHRQ CL, R13 // v0 := table[val0&mask] - MOVW (R9)(R13*2), CX + MOVWQZX (R9)(R13*2), CX // br0.advance(uint8(v0.entry) - MOVB CH, AL - SHLQ CL, R11 - ADDB CL, R12 + MOVBLZX CH, AX + SHLQ CL, R11 + ADDB CL, R12 // val1 := br0.peekTopBits(peekBits) MOVQ DI, CX @@ -60,7 +60,7 @@ skip_fill0: SHRQ CL, R13 // v1 := table[val1&mask] - MOVW (R9)(R13*2), CX + MOVWQZX (R9)(R13*2), CX // br0.advance(uint8(v1.entry)) MOVB CH, AH @@ -104,12 +104,12 @@ skip_fill1: SHRQ CL, R13 // v0 := table[val0&mask] - MOVW (R9)(R13*2), CX + MOVWQZX (R9)(R13*2), CX // br1.advance(uint8(v0.entry) - MOVB CH, AL - SHLQ CL, R11 - ADDB CL, R12 + MOVBLZX CH, AX + SHLQ CL, R11 + ADDB CL, R12 // val1 := br1.peekTopBits(peekBits) MOVQ DI, CX @@ -117,7 +117,7 @@ skip_fill1: SHRQ CL, R13 // v1 := table[val1&mask] - MOVW (R9)(R13*2), CX + MOVWQZX (R9)(R13*2), CX // br1.advance(uint8(v1.entry)) MOVB CH, AH @@ -161,12 +161,12 @@ skip_fill2: SHRQ CL, R13 // v0 := table[val0&mask] - MOVW (R9)(R13*2), CX + MOVWQZX (R9)(R13*2), CX // br2.advance(uint8(v0.entry) - MOVB CH, AL - SHLQ CL, R11 - ADDB CL, R12 + MOVBLZX CH, AX + SHLQ CL, R11 + ADDB CL, R12 // val1 := br2.peekTopBits(peekBits) MOVQ DI, CX @@ -174,7 +174,7 @@ skip_fill2: SHRQ CL, R13 // v1 := table[val1&mask] - MOVW (R9)(R13*2), CX + MOVWQZX (R9)(R13*2), CX // br2.advance(uint8(v1.entry)) MOVB CH, AH @@ -218,12 +218,12 @@ skip_fill3: SHRQ CL, R13 // v0 := table[val0&mask] - MOVW (R9)(R13*2), CX + MOVWQZX (R9)(R13*2), CX // br3.advance(uint8(v0.entry) - MOVB CH, AL - SHLQ CL, R11 - ADDB CL, R12 + MOVBLZX CH, AX + SHLQ CL, R11 + ADDB CL, R12 // val1 := br3.peekTopBits(peekBits) MOVQ DI, CX @@ -231,7 +231,7 @@ skip_fill3: SHRQ CL, R13 // v1 := table[val1&mask] - MOVW (R9)(R13*2), CX + MOVWQZX (R9)(R13*2), CX // br3.advance(uint8(v1.entry)) MOVB CH, AH @@ -259,13 +259,13 @@ skip_fill3: // 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 + 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: @@ -278,35 +278,35 @@ main_loop: MOVBQZX 40(R10), R12 CMPQ R12, $0x20 JBE skip_fill0 - MOVQ 24(R10), R13 + MOVQ 24(R10), AX SUBQ $0x20, R12 - SUBQ $0x04, R13 - MOVQ (R10), R14 + SUBQ $0x04, AX + MOVQ (R10), R13 // b.value |= uint64(low) << (b.bitsRead & 63) - MOVL (R13)(R14*1), R14 + MOVL (AX)(R13*1), R13 MOVQ R12, CX - SHLQ CL, R14 - MOVQ R13, 24(R10) - ORQ R14, R11 + SHLQ CL, R13 + MOVQ AX, 24(R10) + ORQ R13, R11 // exhausted += (br0.off < 4) - CMPQ R13, $0x04 + CMPQ AX, $0x04 ADCB $+0, DL skip_fill0: // val0 := br0.peekTopBits(peekBits) - MOVQ R11, R13 + MOVQ R11, AX MOVQ DI, CX - SHRQ CL, R13 + SHRQ CL, AX // v0 := table[val0&mask] - MOVW (R9)(R13*2), CX + MOVWQZX (R9)(AX*2), CX // br0.advance(uint8(v0.entry) - MOVB CH, AL - SHLQ CL, R11 - ADDB CL, R12 + MOVBLZX CH, AX + SHLQ CL, R11 + ADDB CL, R12 // val1 := br0.peekTopBits(peekBits) MOVQ R11, R13 @@ -314,7 +314,7 @@ skip_fill0: SHRQ CL, R13 // v1 := table[val0&mask] - MOVW (R9)(R13*2), CX + MOVWQZX (R9)(R13*2), CX // br0.advance(uint8(v1.entry) MOVB CH, AH @@ -328,7 +328,7 @@ skip_fill0: SHRQ CL, R13 // v2 := table[val0&mask] - MOVW (R9)(R13*2), CX + MOVWQZX (R9)(R13*2), CX // br0.advance(uint8(v2.entry) MOVB CH, AH @@ -341,7 +341,7 @@ skip_fill0: SHRQ CL, R13 // v3 := table[val0&mask] - MOVW (R9)(R13*2), CX + MOVWQZX (R9)(R13*2), CX // br0.advance(uint8(v3.entry) MOVB CH, AL @@ -365,35 +365,35 @@ skip_fill0: MOVBQZX 88(R10), R12 CMPQ R12, $0x20 JBE skip_fill1 - MOVQ 72(R10), R13 + MOVQ 72(R10), AX SUBQ $0x20, R12 - SUBQ $0x04, R13 - MOVQ 48(R10), R14 + SUBQ $0x04, AX + MOVQ 48(R10), R13 // b.value |= uint64(low) << (b.bitsRead & 63) - MOVL (R13)(R14*1), R14 + MOVL (AX)(R13*1), R13 MOVQ R12, CX - SHLQ CL, R14 - MOVQ R13, 72(R10) - ORQ R14, R11 + SHLQ CL, R13 + MOVQ AX, 72(R10) + ORQ R13, R11 // exhausted += (br1.off < 4) - CMPQ R13, $0x04 + CMPQ AX, $0x04 ADCB $+0, DL skip_fill1: // val0 := br1.peekTopBits(peekBits) - MOVQ R11, R13 + MOVQ R11, AX MOVQ DI, CX - SHRQ CL, R13 + SHRQ CL, AX // v0 := table[val0&mask] - MOVW (R9)(R13*2), CX + MOVWQZX (R9)(AX*2), CX // br1.advance(uint8(v0.entry) - MOVB CH, AL - SHLQ CL, R11 - ADDB CL, R12 + MOVBLZX CH, AX + SHLQ CL, R11 + ADDB CL, R12 // val1 := br1.peekTopBits(peekBits) MOVQ R11, R13 @@ -401,7 +401,7 @@ skip_fill1: SHRQ CL, R13 // v1 := table[val0&mask] - MOVW (R9)(R13*2), CX + MOVWQZX (R9)(R13*2), CX // br1.advance(uint8(v1.entry) MOVB CH, AH @@ -415,7 +415,7 @@ skip_fill1: SHRQ CL, R13 // v2 := table[val0&mask] - MOVW (R9)(R13*2), CX + MOVWQZX (R9)(R13*2), CX // br1.advance(uint8(v2.entry) MOVB CH, AH @@ -428,7 +428,7 @@ skip_fill1: SHRQ CL, R13 // v3 := table[val0&mask] - MOVW (R9)(R13*2), CX + MOVWQZX (R9)(R13*2), CX // br1.advance(uint8(v3.entry) MOVB CH, AL @@ -452,35 +452,35 @@ skip_fill1: MOVBQZX 136(R10), R12 CMPQ R12, $0x20 JBE skip_fill2 - MOVQ 120(R10), R13 + MOVQ 120(R10), AX SUBQ $0x20, R12 - SUBQ $0x04, R13 - MOVQ 96(R10), R14 + SUBQ $0x04, AX + MOVQ 96(R10), R13 // b.value |= uint64(low) << (b.bitsRead & 63) - MOVL (R13)(R14*1), R14 + MOVL (AX)(R13*1), R13 MOVQ R12, CX - SHLQ CL, R14 - MOVQ R13, 120(R10) - ORQ R14, R11 + SHLQ CL, R13 + MOVQ AX, 120(R10) + ORQ R13, R11 // exhausted += (br2.off < 4) - CMPQ R13, $0x04 + CMPQ AX, $0x04 ADCB $+0, DL skip_fill2: // val0 := br2.peekTopBits(peekBits) - MOVQ R11, R13 + MOVQ R11, AX MOVQ DI, CX - SHRQ CL, R13 + SHRQ CL, AX // v0 := table[val0&mask] - MOVW (R9)(R13*2), CX + MOVWQZX (R9)(AX*2), CX // br2.advance(uint8(v0.entry) - MOVB CH, AL - SHLQ CL, R11 - ADDB CL, R12 + MOVBLZX CH, AX + SHLQ CL, R11 + ADDB CL, R12 // val1 := br2.peekTopBits(peekBits) MOVQ R11, R13 @@ -488,7 +488,7 @@ skip_fill2: SHRQ CL, R13 // v1 := table[val0&mask] - MOVW (R9)(R13*2), CX + MOVWQZX (R9)(R13*2), CX // br2.advance(uint8(v1.entry) MOVB CH, AH @@ -502,7 +502,7 @@ skip_fill2: SHRQ CL, R13 // v2 := table[val0&mask] - MOVW (R9)(R13*2), CX + MOVWQZX (R9)(R13*2), CX // br2.advance(uint8(v2.entry) MOVB CH, AH @@ -515,7 +515,7 @@ skip_fill2: SHRQ CL, R13 // v3 := table[val0&mask] - MOVW (R9)(R13*2), CX + MOVWQZX (R9)(R13*2), CX // br2.advance(uint8(v3.entry) MOVB CH, AL @@ -539,35 +539,35 @@ skip_fill2: MOVBQZX 184(R10), R12 CMPQ R12, $0x20 JBE skip_fill3 - MOVQ 168(R10), R13 + MOVQ 168(R10), AX SUBQ $0x20, R12 - SUBQ $0x04, R13 - MOVQ 144(R10), R14 + SUBQ $0x04, AX + MOVQ 144(R10), R13 // b.value |= uint64(low) << (b.bitsRead & 63) - MOVL (R13)(R14*1), R14 + MOVL (AX)(R13*1), R13 MOVQ R12, CX - SHLQ CL, R14 - MOVQ R13, 168(R10) - ORQ R14, R11 + SHLQ CL, R13 + MOVQ AX, 168(R10) + ORQ R13, R11 // exhausted += (br3.off < 4) - CMPQ R13, $0x04 + CMPQ AX, $0x04 ADCB $+0, DL skip_fill3: // val0 := br3.peekTopBits(peekBits) - MOVQ R11, R13 + MOVQ R11, AX MOVQ DI, CX - SHRQ CL, R13 + SHRQ CL, AX // v0 := table[val0&mask] - MOVW (R9)(R13*2), CX + MOVWQZX (R9)(AX*2), CX // br3.advance(uint8(v0.entry) - MOVB CH, AL - SHLQ CL, R11 - ADDB CL, R12 + MOVBLZX CH, AX + SHLQ CL, R11 + ADDB CL, R12 // val1 := br3.peekTopBits(peekBits) MOVQ R11, R13 @@ -575,7 +575,7 @@ skip_fill3: SHRQ CL, R13 // v1 := table[val0&mask] - MOVW (R9)(R13*2), CX + MOVWQZX (R9)(R13*2), CX // br3.advance(uint8(v1.entry) MOVB CH, AH @@ -589,7 +589,7 @@ skip_fill3: SHRQ CL, R13 // v2 := table[val0&mask] - MOVW (R9)(R13*2), CX + MOVWQZX (R9)(R13*2), CX // br3.advance(uint8(v2.entry) MOVB CH, AH @@ -602,7 +602,7 @@ skip_fill3: SHRQ CL, R13 // v3 := table[val0&mask] - MOVW (R9)(R13*2), CX + MOVWQZX (R9)(R13*2), CX // br3.advance(uint8(v3.entry) MOVB CH, AL @@ -667,7 +667,7 @@ bitReader_fillFast_1_end: MOVQ DI, CX MOVQ R10, R12 SHRQ CL, R12 - MOVW (SI)(R12*2), CX + MOVWQZX (SI)(R12*2), CX MOVB CH, AL MOVBQZX CL, CX ADDQ CX, R11 @@ -675,7 +675,7 @@ bitReader_fillFast_1_end: MOVQ DI, CX MOVQ R10, R12 SHRQ CL, R12 - MOVW (SI)(R12*2), CX + MOVWQZX (SI)(R12*2), CX MOVB CH, AH MOVBQZX CL, CX ADDQ CX, R11 @@ -694,7 +694,7 @@ bitReader_fillFast_2_end: MOVQ DI, CX MOVQ R10, R12 SHRQ CL, R12 - MOVW (SI)(R12*2), CX + MOVWQZX (SI)(R12*2), CX MOVB CH, AH MOVBQZX CL, CX ADDQ CX, R11 @@ -702,7 +702,7 @@ bitReader_fillFast_2_end: MOVQ DI, CX MOVQ R10, R12 SHRQ CL, R12 - MOVW (SI)(R12*2), CX + MOVWQZX (SI)(R12*2), CX MOVB CH, AL MOVBQZX CL, CX ADDQ CX, R11 @@ -769,13 +769,13 @@ main_loop: bitReader_fillFast_1_end: SHRXQ DI, R10, CX - MOVW (SI)(CX*2), CX + MOVWQZX (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 + MOVWQZX (SI)(CX*2), CX MOVB CH, AH MOVBQZX CL, CX ADDQ CX, R11 @@ -791,13 +791,13 @@ bitReader_fillFast_1_end: bitReader_fillFast_2_end: SHRXQ DI, R10, CX - MOVW (SI)(CX*2), CX + MOVWQZX (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 + MOVWQZX (SI)(CX*2), CX MOVB CH, AL MOVBQZX CL, CX ADDQ CX, R11 diff --git a/vendor/github.com/klauspost/compress/huff0/decompress_arm64.s b/vendor/github.com/klauspost/compress/huff0/decompress_arm64.s index e16012721a..357df28e9d 100644 --- a/vendor/github.com/klauspost/compress/huff0/decompress_arm64.s +++ b/vendor/github.com/klauspost/compress/huff0/decompress_arm64.s @@ -1,7 +1,7 @@ // Code generated by command: go run gen.go -out ../decompress.s -arch amd64,arm64 -pkg=huff0. DO NOT EDIT. // EXPERIMENTAL arm64 output lowered from an amd64 avo program. -//go:build arm64 && !appengine && !noasm && gc +//go:build arm64 && (!appengine && !noasm && gc) // func decompress4x_main_loop_amd64(ctx *decompress4xContext) TEXT ·decompress4x_main_loop_arm64(SB), $0-8 @@ -32,8 +32,7 @@ main_loop: MOVD (R9), R12 // b.value |= uint64(low) << (b.bitsRead & 63) - ADD R12, R0, R15 - MOVWU (R15), R12 + MOVWU (R0)(R12), R12 MOVD R11, R1 LSL R1, R12, R12 MOVD R0, 24(R9) @@ -51,12 +50,10 @@ skip_fill0: LSR R1, R12, R12 // v0 := table[val0&mask] - ADD R12<<1, R8, R15 - MOVHU (R15), R1 + MOVHU (R8)(R12<<1), R1 // br0.advance(uint8(v0.entry) - UBFX $8, R1, $8, R16 - BFI $0, R16, $8, R0 + UBFX $8, R1, $8, R0 LSL R1, R10, R10 ADD R1, R11, R15 BFI $0, R15, $8, R11 @@ -67,8 +64,7 @@ skip_fill0: LSR R1, R12, R12 // v1 := table[val1&mask] - ADD R12<<1, R8, R15 - MOVHU (R15), R1 + MOVHU (R8)(R12<<1), R1 // br0.advance(uint8(v1.entry)) UBFX $8, R1, $8, R16 @@ -97,8 +93,7 @@ skip_fill0: MOVD 48(R9), R12 // b.value |= uint64(low) << (b.bitsRead & 63) - ADD R12, R0, R15 - MOVWU (R15), R12 + MOVWU (R0)(R12), R12 MOVD R11, R1 LSL R1, R12, R12 MOVD R0, 72(R9) @@ -116,12 +111,10 @@ skip_fill1: LSR R1, R12, R12 // v0 := table[val0&mask] - ADD R12<<1, R8, R15 - MOVHU (R15), R1 + MOVHU (R8)(R12<<1), R1 // br1.advance(uint8(v0.entry) - UBFX $8, R1, $8, R16 - BFI $0, R16, $8, R0 + UBFX $8, R1, $8, R0 LSL R1, R10, R10 ADD R1, R11, R15 BFI $0, R15, $8, R11 @@ -132,8 +125,7 @@ skip_fill1: LSR R1, R12, R12 // v1 := table[val1&mask] - ADD R12<<1, R8, R15 - MOVHU (R15), R1 + MOVHU (R8)(R12<<1), R1 // br1.advance(uint8(v1.entry)) UBFX $8, R1, $8, R16 @@ -145,8 +137,7 @@ skip_fill1: // these two writes get coalesced // out[id * dstEvery + 0] = uint8(v0.entry >> 8) // out[id * dstEvery + 1] = uint8(v1.entry >> 8) - ADD R7, R3, R15 - MOVH R0, (R15) + MOVH R0, (R3)(R7) // update the bitreader structure MOVD R10, 80(R9) @@ -163,8 +154,7 @@ skip_fill1: MOVD 96(R9), R12 // b.value |= uint64(low) << (b.bitsRead & 63) - ADD R12, R0, R15 - MOVWU (R15), R12 + MOVWU (R0)(R12), R12 MOVD R11, R1 LSL R1, R12, R12 MOVD R0, 120(R9) @@ -182,12 +172,10 @@ skip_fill2: LSR R1, R12, R12 // v0 := table[val0&mask] - ADD R12<<1, R8, R15 - MOVHU (R15), R1 + MOVHU (R8)(R12<<1), R1 // br2.advance(uint8(v0.entry) - UBFX $8, R1, $8, R16 - BFI $0, R16, $8, R0 + UBFX $8, R1, $8, R0 LSL R1, R10, R10 ADD R1, R11, R15 BFI $0, R15, $8, R11 @@ -198,8 +186,7 @@ skip_fill2: LSR R1, R12, R12 // v1 := table[val1&mask] - ADD R12<<1, R8, R15 - MOVHU (R15), R1 + MOVHU (R8)(R12<<1), R1 // br2.advance(uint8(v1.entry)) UBFX $8, R1, $8, R16 @@ -211,8 +198,7 @@ skip_fill2: // these two writes get coalesced // out[id * dstEvery + 0] = uint8(v0.entry >> 8) // out[id * dstEvery + 1] = uint8(v1.entry >> 8) - ADD R7<<1, R3, R15 - MOVH R0, (R15) + MOVH R0, (R3)(R7<<1) // update the bitreader structure MOVD R10, 128(R9) @@ -229,8 +215,7 @@ skip_fill2: MOVD 144(R9), R12 // b.value |= uint64(low) << (b.bitsRead & 63) - ADD R12, R0, R15 - MOVWU (R15), R12 + MOVWU (R0)(R12), R12 MOVD R11, R1 LSL R1, R12, R12 MOVD R0, 168(R9) @@ -248,12 +233,10 @@ skip_fill3: LSR R1, R12, R12 // v0 := table[val0&mask] - ADD R12<<1, R8, R15 - MOVHU (R15), R1 + MOVHU (R8)(R12<<1), R1 // br3.advance(uint8(v0.entry) - UBFX $8, R1, $8, R16 - BFI $0, R16, $8, R0 + UBFX $8, R1, $8, R0 LSL R1, R10, R10 ADD R1, R11, R15 BFI $0, R15, $8, R11 @@ -264,8 +247,7 @@ skip_fill3: LSR R1, R12, R12 // v1 := table[val1&mask] - ADD R12<<1, R8, R15 - MOVHU (R15), R1 + MOVHU (R8)(R12<<1), R1 // br3.advance(uint8(v1.entry)) UBFX $8, R1, $8, R16 @@ -278,16 +260,13 @@ skip_fill3: // out[id * dstEvery + 0] = uint8(v0.entry >> 8) // out[id * dstEvery + 1] = uint8(v1.entry >> 8) ADD R7<<1, R7, R1 - ADD R1, R3, R15 - MOVH R0, (R15) + MOVH R0, (R3)(R1) // update the bitreader structure MOVD R10, 176(R9) MOVB R11, 184(R9) ADD $0x02, R3, R3 - AND $0xff, R2, R15 - AND $0xff, R2, R16 - TST R16, R15 + TST $0xff, R2 BEQ main_loop MOVD ctx+0(FP), R0 MOVD 16(R0), R16 @@ -299,13 +278,13 @@ skip_fill3: // func decompress4x_8b_main_loop_amd64(ctx *decompress4xContext) TEXT ·decompress4x_8b_main_loop_arm64(SB), $0-8 // Preload values - MOVD ctx+0(FP), R1 - MOVBU 8(R1), R6 - MOVD 16(R1), R3 - MOVD 48(R1), R5 - MOVD 24(R1), R7 - MOVD 32(R1), R8 - MOVD (R1), R9 + MOVD ctx+0(FP), R0 + MOVBU 8(R0), R6 + MOVD 16(R0), R3 + MOVD 48(R0), R5 + MOVD 24(R0), R7 + MOVD 32(R0), R8 + MOVD (R0), R9 // Main loop main_loop: @@ -319,37 +298,34 @@ main_loop: MOVBU 40(R9), R11 CMP $0x20, R11 BLS skip_fill0 - MOVD 24(R9), R12 + MOVD 24(R9), R0 SUB $0x20, R11, R11 - SUB $0x04, R12, R12 - MOVD (R9), R13 + SUB $0x04, R0, R0 + MOVD (R9), R12 // b.value |= uint64(low) << (b.bitsRead & 63) - ADD R13, R12, R15 - MOVWU (R15), R13 + MOVWU (R0)(R12), R12 MOVD R11, R1 - LSL R1, R13, R13 - MOVD R12, 24(R9) - ORR R13, R10, R10 + LSL R1, R12, R12 + MOVD R0, 24(R9) + ORR R12, R10, R10 // exhausted += (br0.off < 4) - CMP $0x04, R12 + CMP $0x04, R0 CSINC HS, R2, R2, R16 BFI $0, R16, $8, R2 skip_fill0: // val0 := br0.peekTopBits(peekBits) - MOVD R10, R12 + MOVD R10, R0 MOVD R6, R1 - LSR R1, R12, R12 + LSR R1, R0, R0 // v0 := table[val0&mask] - ADD R12<<1, R8, R15 - MOVHU (R15), R1 + MOVHU (R8)(R0<<1), R1 // br0.advance(uint8(v0.entry) - UBFX $8, R1, $8, R16 - BFI $0, R16, $8, R0 + UBFX $8, R1, $8, R0 LSL R1, R10, R10 ADD R1, R11, R15 BFI $0, R15, $8, R11 @@ -360,8 +336,7 @@ skip_fill0: LSR R1, R12, R12 // v1 := table[val0&mask] - ADD R12<<1, R8, R15 - MOVHU (R15), R1 + MOVHU (R8)(R12<<1), R1 // br0.advance(uint8(v1.entry) UBFX $8, R1, $8, R16 @@ -377,8 +352,7 @@ skip_fill0: LSR R1, R12, R12 // v2 := table[val0&mask] - ADD R12<<1, R8, R15 - MOVHU (R15), R1 + MOVHU (R8)(R12<<1), R1 // br0.advance(uint8(v2.entry) UBFX $8, R1, $8, R16 @@ -393,8 +367,7 @@ skip_fill0: LSR R1, R12, R12 // v3 := table[val0&mask] - ADD R12<<1, R8, R15 - MOVHU (R15), R1 + MOVHU (R8)(R12<<1), R1 // br0.advance(uint8(v3.entry) UBFX $8, R1, $8, R16 @@ -420,37 +393,34 @@ skip_fill0: MOVBU 88(R9), R11 CMP $0x20, R11 BLS skip_fill1 - MOVD 72(R9), R12 + MOVD 72(R9), R0 SUB $0x20, R11, R11 - SUB $0x04, R12, R12 - MOVD 48(R9), R13 + SUB $0x04, R0, R0 + MOVD 48(R9), R12 // b.value |= uint64(low) << (b.bitsRead & 63) - ADD R13, R12, R15 - MOVWU (R15), R13 + MOVWU (R0)(R12), R12 MOVD R11, R1 - LSL R1, R13, R13 - MOVD R12, 72(R9) - ORR R13, R10, R10 + LSL R1, R12, R12 + MOVD R0, 72(R9) + ORR R12, R10, R10 // exhausted += (br1.off < 4) - CMP $0x04, R12 + CMP $0x04, R0 CSINC HS, R2, R2, R16 BFI $0, R16, $8, R2 skip_fill1: // val0 := br1.peekTopBits(peekBits) - MOVD R10, R12 + MOVD R10, R0 MOVD R6, R1 - LSR R1, R12, R12 + LSR R1, R0, R0 // v0 := table[val0&mask] - ADD R12<<1, R8, R15 - MOVHU (R15), R1 + MOVHU (R8)(R0<<1), R1 // br1.advance(uint8(v0.entry) - UBFX $8, R1, $8, R16 - BFI $0, R16, $8, R0 + UBFX $8, R1, $8, R0 LSL R1, R10, R10 ADD R1, R11, R15 BFI $0, R15, $8, R11 @@ -461,8 +431,7 @@ skip_fill1: LSR R1, R12, R12 // v1 := table[val0&mask] - ADD R12<<1, R8, R15 - MOVHU (R15), R1 + MOVHU (R8)(R12<<1), R1 // br1.advance(uint8(v1.entry) UBFX $8, R1, $8, R16 @@ -478,8 +447,7 @@ skip_fill1: LSR R1, R12, R12 // v2 := table[val0&mask] - ADD R12<<1, R8, R15 - MOVHU (R15), R1 + MOVHU (R8)(R12<<1), R1 // br1.advance(uint8(v2.entry) UBFX $8, R1, $8, R16 @@ -494,8 +462,7 @@ skip_fill1: LSR R1, R12, R12 // v3 := table[val0&mask] - ADD R12<<1, R8, R15 - MOVHU (R15), R1 + MOVHU (R8)(R12<<1), R1 // br1.advance(uint8(v3.entry) UBFX $8, R1, $8, R16 @@ -510,8 +477,7 @@ skip_fill1: // out[id * dstEvery + 1] = uint8(v1.entry >> 8) // out[id * dstEvery + 3] = uint8(v2.entry >> 8) // out[id * dstEvery + 4] = uint8(v3.entry >> 8) - ADD R7, R3, R15 - MOVW R0, (R15) + MOVW R0, (R3)(R7) // update the bitreader structure MOVD R10, 80(R9) @@ -522,37 +488,34 @@ skip_fill1: MOVBU 136(R9), R11 CMP $0x20, R11 BLS skip_fill2 - MOVD 120(R9), R12 + MOVD 120(R9), R0 SUB $0x20, R11, R11 - SUB $0x04, R12, R12 - MOVD 96(R9), R13 + SUB $0x04, R0, R0 + MOVD 96(R9), R12 // b.value |= uint64(low) << (b.bitsRead & 63) - ADD R13, R12, R15 - MOVWU (R15), R13 + MOVWU (R0)(R12), R12 MOVD R11, R1 - LSL R1, R13, R13 - MOVD R12, 120(R9) - ORR R13, R10, R10 + LSL R1, R12, R12 + MOVD R0, 120(R9) + ORR R12, R10, R10 // exhausted += (br2.off < 4) - CMP $0x04, R12 + CMP $0x04, R0 CSINC HS, R2, R2, R16 BFI $0, R16, $8, R2 skip_fill2: // val0 := br2.peekTopBits(peekBits) - MOVD R10, R12 + MOVD R10, R0 MOVD R6, R1 - LSR R1, R12, R12 + LSR R1, R0, R0 // v0 := table[val0&mask] - ADD R12<<1, R8, R15 - MOVHU (R15), R1 + MOVHU (R8)(R0<<1), R1 // br2.advance(uint8(v0.entry) - UBFX $8, R1, $8, R16 - BFI $0, R16, $8, R0 + UBFX $8, R1, $8, R0 LSL R1, R10, R10 ADD R1, R11, R15 BFI $0, R15, $8, R11 @@ -563,8 +526,7 @@ skip_fill2: LSR R1, R12, R12 // v1 := table[val0&mask] - ADD R12<<1, R8, R15 - MOVHU (R15), R1 + MOVHU (R8)(R12<<1), R1 // br2.advance(uint8(v1.entry) UBFX $8, R1, $8, R16 @@ -580,8 +542,7 @@ skip_fill2: LSR R1, R12, R12 // v2 := table[val0&mask] - ADD R12<<1, R8, R15 - MOVHU (R15), R1 + MOVHU (R8)(R12<<1), R1 // br2.advance(uint8(v2.entry) UBFX $8, R1, $8, R16 @@ -596,8 +557,7 @@ skip_fill2: LSR R1, R12, R12 // v3 := table[val0&mask] - ADD R12<<1, R8, R15 - MOVHU (R15), R1 + MOVHU (R8)(R12<<1), R1 // br2.advance(uint8(v3.entry) UBFX $8, R1, $8, R16 @@ -624,37 +584,34 @@ skip_fill2: MOVBU 184(R9), R11 CMP $0x20, R11 BLS skip_fill3 - MOVD 168(R9), R12 + MOVD 168(R9), R0 SUB $0x20, R11, R11 - SUB $0x04, R12, R12 - MOVD 144(R9), R13 + SUB $0x04, R0, R0 + MOVD 144(R9), R12 // b.value |= uint64(low) << (b.bitsRead & 63) - ADD R13, R12, R15 - MOVWU (R15), R13 + MOVWU (R0)(R12), R12 MOVD R11, R1 - LSL R1, R13, R13 - MOVD R12, 168(R9) - ORR R13, R10, R10 + LSL R1, R12, R12 + MOVD R0, 168(R9) + ORR R12, R10, R10 // exhausted += (br3.off < 4) - CMP $0x04, R12 + CMP $0x04, R0 CSINC HS, R2, R2, R16 BFI $0, R16, $8, R2 skip_fill3: // val0 := br3.peekTopBits(peekBits) - MOVD R10, R12 + MOVD R10, R0 MOVD R6, R1 - LSR R1, R12, R12 + LSR R1, R0, R0 // v0 := table[val0&mask] - ADD R12<<1, R8, R15 - MOVHU (R15), R1 + MOVHU (R8)(R0<<1), R1 // br3.advance(uint8(v0.entry) - UBFX $8, R1, $8, R16 - BFI $0, R16, $8, R0 + UBFX $8, R1, $8, R0 LSL R1, R10, R10 ADD R1, R11, R15 BFI $0, R15, $8, R11 @@ -665,8 +622,7 @@ skip_fill3: LSR R1, R12, R12 // v1 := table[val0&mask] - ADD R12<<1, R8, R15 - MOVHU (R15), R1 + MOVHU (R8)(R12<<1), R1 // br3.advance(uint8(v1.entry) UBFX $8, R1, $8, R16 @@ -682,8 +638,7 @@ skip_fill3: LSR R1, R12, R12 // v2 := table[val0&mask] - ADD R12<<1, R8, R15 - MOVHU (R15), R1 + MOVHU (R8)(R12<<1), R1 // br3.advance(uint8(v2.entry) UBFX $8, R1, $8, R16 @@ -698,8 +653,7 @@ skip_fill3: LSR R1, R12, R12 // v3 := table[val0&mask] - ADD R12<<1, R8, R15 - MOVHU (R15), R1 + MOVHU (R8)(R12<<1), R1 // br3.advance(uint8(v3.entry) UBFX $8, R1, $8, R16 @@ -715,16 +669,13 @@ skip_fill3: // out[id * dstEvery + 3] = uint8(v2.entry >> 8) // out[id * dstEvery + 4] = uint8(v3.entry >> 8) ADD R7<<1, R7, R1 - ADD R1, R3, R15 - MOVW R0, (R15) + MOVW R0, (R3)(R1) // update the bitreader structure MOVD R10, 176(R9) MOVB R11, 184(R9) ADD $0x04, R3, R3 - AND $0xff, R2, R15 - AND $0xff, R2, R16 - TST R16, R15 + TST $0xff, R2 BEQ main_loop MOVD ctx+0(FP), R0 MOVD 16(R0), R16 @@ -761,8 +712,7 @@ main_loop: BLT bitReader_fillFast_1_end SUB $0x20, R10, R10 SUB $0x04, R8, R8 - ADD R8, R7, R15 - MOVWU (R15), R11 + MOVWU (R7)(R8), R11 MOVD R10, R1 LSL R1, R11, R11 ORR R11, R9, R9 @@ -771,8 +721,7 @@ bitReader_fillFast_1_end: MOVD R6, R1 MOVD R9, R11 LSR R1, R11, R11 - ADD R11<<1, R5, R15 - MOVHU (R15), R1 + MOVHU (R5)(R11<<1), R1 UBFX $8, R1, $8, R16 BFI $0, R16, $8, R0 MOVBU R1, R1 @@ -781,8 +730,7 @@ bitReader_fillFast_1_end: MOVD R6, R1 MOVD R9, R11 LSR R1, R11, R11 - ADD R11<<1, R5, R15 - MOVHU (R15), R1 + MOVHU (R5)(R11<<1), R1 UBFX $8, R1, $8, R16 BFI $8, R16, $8, R0 MOVBU R1, R1 @@ -793,8 +741,7 @@ bitReader_fillFast_1_end: BLT bitReader_fillFast_2_end SUB $0x20, R10, R10 SUB $0x04, R8, R8 - ADD R8, R7, R15 - MOVWU (R15), R11 + MOVWU (R7)(R8), R11 MOVD R10, R1 LSL R1, R11, R11 ORR R11, R9, R9 @@ -803,8 +750,7 @@ bitReader_fillFast_2_end: MOVD R6, R1 MOVD R9, R11 LSR R1, R11, R11 - ADD R11<<1, R5, R15 - MOVHU (R15), R1 + MOVHU (R5)(R11<<1), R1 UBFX $8, R1, $8, R16 BFI $8, R16, $8, R0 MOVBU R1, R1 @@ -813,8 +759,7 @@ bitReader_fillFast_2_end: MOVD R6, R1 MOVD R9, R11 LSR R1, R11, R11 - ADD R11<<1, R5, R15 - MOVHU (R15), R1 + MOVHU (R5)(R11<<1), R1 UBFX $8, R1, $8, R16 BFI $0, R16, $8, R0 MOVBU R1, R1 diff --git a/vendor/github.com/klauspost/compress/zstd/fse_decoder_arm64.s b/vendor/github.com/klauspost/compress/zstd/fse_decoder_arm64.s index 77ee3913f0..b6b4607878 100644 --- a/vendor/github.com/klauspost/compress/zstd/fse_decoder_arm64.s +++ b/vendor/github.com/klauspost/compress/zstd/fse_decoder_arm64.s @@ -1,7 +1,7 @@ // Code generated by command: go run gen_fse.go -out ../fse_decoder.s -arch amd64,arm64 -pkg=zstd. DO NOT EDIT. // EXPERIMENTAL arm64 output lowered from an amd64 avo program. -//go:build arm64 && !appengine && !noasm && gc && !noasm +//go:build arm64 && (!appengine && !noasm && gc && !noasm) // func buildDtable_asm(s *fseDecoder, ctx *buildDtableAsmContext) int TEXT ·buildDtable_asm(SB), $0-24 @@ -26,11 +26,10 @@ TEXT ·buildDtable_asm(SB), $0-24 JMP init_main_loop_condition init_main_loop: - ADD R8<<1, R1, R15 - MOVH (R15), R9 - AND $0xffff, R9, R15 + MOVH (R1)(R8<<1), R9 MOVD $-1, R16 AND $0xffff, R16, R16 + AND $0xffff, R9, R15 CMP R16, R15 BNE do_not_update_high_threshold ADD R7<<3, R5, R15 @@ -39,8 +38,7 @@ init_main_loop: MOVD $0x0000000000000001, R9 do_not_update_high_threshold: - ADD R8<<1, R3, R15 - MOVH R9, (R15) + MOVH R9, (R3)(R8<<1) ADD $1, R8, R8 init_main_loop_condition: @@ -64,8 +62,7 @@ init_main_loop_condition: spread_main_loop: MOVD $0, R12 - ADD R11<<1, R1, R15 - MOVH (R15), R13 + MOVH (R1)(R11<<1), R13 JMP spread_inner_loop_condition spread_inner_loop: @@ -102,11 +99,9 @@ spread_check_ok: build_table_main_table: ADD R6<<3, R5, R15 MOVBU 1(R15), R1 - ADD R1<<1, R3, R15 - MOVHU (R15), R7 + MOVHU (R3)(R1<<1), R7 ADD $1, R7, R8 - ADD R1<<1, R3, R15 - MOVH R8, (R15) + MOVH R8, (R3)(R1<<1) MOVD R7, R8 CLZ R8, R16 MOVD $63, R8 @@ -129,12 +124,10 @@ build_table_main_table: RET build_table_check1_ok: - AND $0xff, R1, R15 - AND $0xff, R1, R16 - TST R16, R15 + TST $0xff, R1 BNE build_table_check2_ok - AND $0xffff, R7, R15 AND $0xffff, R6, R16 + AND $0xffff, R7, R15 CMP R16, R15 BNE build_table_check2_ok MOVD ctx+8(FP), R0 diff --git a/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash.go b/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash.go index fc40c82001..9c0cbc1681 100644 --- a/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash.go +++ b/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash.go @@ -52,6 +52,11 @@ func (d *Digest) Reset() { d.n = 0 } +// maxAsmSize bounds the input handed to a single writeBlocks call. Assembly is +// never preemptible, so an unbounded call holds every P in stop-the-world for +// its duration. Whole 32-byte blocks, so Write can feed it in pieces. +const maxAsmSize = 128 << 10 // 4096 whole 32-byte blocks + // Size always returns 8 bytes. func (d *Digest) Size() int { return 8 } @@ -83,6 +88,13 @@ func (d *Digest) Write(b []byte) (n int, err error) { d.n = 0 } + for len(b) >= maxAsmSize { + // Assembly is not preemptible, so hashing a large buffer in one call + // blocks every stop-the-world for the whole call. Feed it in chunks. + writeBlocks(d, b[:maxAsmSize]) + b = b[maxAsmSize:] + } + if len(b) >= 32 { // One or more full blocks left. nw := writeBlocks(d, b) diff --git a/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_amd64.s b/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_amd64.s index ddb63aa91b..e4c688efd3 100644 --- a/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_amd64.s +++ b/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_amd64.s @@ -175,7 +175,11 @@ finalize: RET // func writeBlocks(d *Digest, b []byte) int -TEXT ·writeBlocks(SB), NOSPLIT|NOFRAME, $0-40 +// +// Deliberately not NOSPLIT: the stack check is the preemption point that lets a +// stop-the-world proceed between chunks. The frame must be >= abi.StackSmall +// (128), or the assembler marks this leaf NOSPLIT anyway and drops that check. +TEXT ·writeBlocks(SB), $128-40 // Load fixed primes needed for round. MOVQ ·primes+0(SB), prime1 MOVQ ·primes+8(SB), prime2 diff --git a/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_arm64.s b/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_arm64.s index ae7d4d3295..e88cc2fc91 100644 --- a/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_arm64.s +++ b/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_arm64.s @@ -163,7 +163,11 @@ finalize: RET // func writeBlocks(s *Digest, b []byte) int -TEXT ·writeBlocks(SB), NOSPLIT|NOFRAME, $0-40 +// +// Deliberately not NOSPLIT: the stack check is the preemption point that lets a +// stop-the-world proceed between chunks. The frame must be >= abi.StackSmall +// (128), or the assembler marks this leaf NOSPLIT anyway and drops that check. +TEXT ·writeBlocks(SB), $128-40 LDP ·primes+0(SB), (prime1, prime2) // Load state. Assume v[1-4] are stored contiguously. diff --git a/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.s b/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.s index 4d3188ff49..9c4f8d3528 100644 --- a/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.s +++ b/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.s @@ -55,43 +55,43 @@ sequenceDecs_decode_amd64_fill_check_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 + MOVQ R9, AX + MOVQ BX, CX + MOVQ DX, R15 + SHLQ CL, R15 + MOVBLZX AH, CX + 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 + MOVQ R8, AX + MOVQ BX, CX + MOVQ DX, R15 + SHLQ CL, R15 + MOVBLZX AH, CX + 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) @@ -126,22 +126,22 @@ sequenceDecs_decode_amd64_fill_2_check_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 + MOVQ DI, AX + MOVQ BX, CX + MOVQ DX, R15 + SHLQ CL, R15 + MOVBLZX AH, CX + 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) @@ -163,7 +163,7 @@ sequenceDecs_decode_amd64_ll_update_zero: MOVQ CX, BX ROLQ CL, R15 MOVL $0x00000001, BP - MOVB R14, CL + MOVBLZX R14, CX SHLL CL, BP DECL BP ANDQ BP, R15 @@ -182,7 +182,7 @@ sequenceDecs_decode_amd64_ll_update_zero: MOVQ CX, BX ROLQ CL, R15 MOVL $0x00000001, BP - MOVB R14, CL + MOVBLZX R14, CX SHLL CL, BP DECL BP ANDQ BP, R15 @@ -201,7 +201,7 @@ sequenceDecs_decode_amd64_ll_update_zero: MOVQ CX, BX ROLQ CL, R15 MOVL $0x00000001, BP - MOVB R14, CL + MOVBLZX R14, CX SHLL CL, BP DECL BP ANDQ BP, R15 @@ -383,64 +383,64 @@ sequenceDecs_decode_56_amd64_fill_check_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 + MOVQ R9, AX + MOVQ BX, CX + MOVQ DX, R15 + SHLQ CL, R15 + MOVBLZX AH, CX + 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 + MOVQ R8, AX + MOVQ BX, CX + MOVQ DX, R15 + SHLQ CL, R15 + MOVBLZX AH, CX + 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 + MOVQ DI, AX + MOVQ BX, CX + MOVQ DX, R15 + SHLQ CL, R15 + MOVBLZX AH, CX + 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) @@ -462,7 +462,7 @@ sequenceDecs_decode_56_amd64_ll_update_zero: MOVQ CX, BX ROLQ CL, R15 MOVL $0x00000001, BP - MOVB R14, CL + MOVBLZX R14, CX SHLL CL, BP DECL BP ANDQ BP, R15 @@ -481,7 +481,7 @@ sequenceDecs_decode_56_amd64_ll_update_zero: MOVQ CX, BX ROLQ CL, R15 MOVL $0x00000001, BP - MOVB R14, CL + MOVBLZX R14, CX SHLL CL, BP DECL BP ANDQ BP, R15 @@ -500,7 +500,7 @@ sequenceDecs_decode_56_amd64_ll_update_zero: MOVQ CX, BX ROLQ CL, R15 MOVL $0x00000001, BP - MOVB R14, CL + MOVBLZX R14, CX SHLL CL, BP DECL BP ANDQ BP, R15 @@ -1258,13 +1258,13 @@ copy_4_small: 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 + MOVWQZX (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 @@ -1327,13 +1327,13 @@ copy_5_move_1or2: 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 + MOVWQZX (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 @@ -1384,12 +1384,12 @@ 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 + MOVBQZX (R11), R12 + MOVB R12, (BX) + INCQ R11 + INCQ BX + DECQ R13 + JNZ copy_slow_3 handle_loop: ADDQ $0x18, AX @@ -1494,13 +1494,13 @@ copy_1_move_1or2: 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 + MOVWQZX (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 @@ -1563,13 +1563,13 @@ copy_4_small: 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 + MOVWQZX (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 @@ -1632,13 +1632,13 @@ copy_5_move_1or2: 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 + MOVWQZX (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 @@ -1707,13 +1707,13 @@ copy_2_move_1or2: 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 + MOVWQZX (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 @@ -1740,12 +1740,12 @@ 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 + MOVBQZX (R11), R12 + MOVB R12, (BX) + INCQ R11 + INCQ BX + DECQ R13 + JNZ copy_slow_3 handle_loop: ADDQ $0x18, AX @@ -1853,43 +1853,43 @@ sequenceDecs_decodeSync_amd64_fill_check_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 + MOVQ R9, AX + MOVQ BX, CX + MOVQ DX, R14 + SHLQ CL, R14 + MOVBLZX AH, CX + 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 + MOVQ R8, AX + MOVQ BX, CX + MOVQ DX, R14 + SHLQ CL, R14 + MOVBLZX AH, CX + 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) @@ -1924,22 +1924,22 @@ sequenceDecs_decodeSync_amd64_fill_2_check_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 + MOVQ DI, AX + MOVQ BX, CX + MOVQ DX, R14 + SHLQ CL, R14 + MOVBLZX AH, CX + 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) @@ -1961,7 +1961,7 @@ sequenceDecs_decodeSync_amd64_ll_update_zero: MOVQ CX, BX ROLQ CL, R14 MOVL $0x00000001, R15 - MOVB R13, CL + MOVBLZX R13, CX SHLL CL, R15 DECL R15 ANDQ R15, R14 @@ -1980,7 +1980,7 @@ sequenceDecs_decodeSync_amd64_ll_update_zero: MOVQ CX, BX ROLQ CL, R14 MOVL $0x00000001, R15 - MOVB R13, CL + MOVBLZX R13, CX SHLL CL, R15 DECL R15 ANDQ R15, R14 @@ -1999,7 +1999,7 @@ sequenceDecs_decodeSync_amd64_ll_update_zero: MOVQ CX, BX ROLQ CL, R14 MOVL $0x00000001, R15 - MOVB R13, CL + MOVBLZX R13, CX SHLL CL, R15 DECL R15 ANDQ R15, R14 @@ -2143,13 +2143,13 @@ copy_4_small: 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 + MOVWQZX (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 @@ -2209,13 +2209,13 @@ copy_5_move_1or2: 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 + MOVWQZX (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 @@ -2266,12 +2266,12 @@ 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 + MOVBQZX (AX), CX + MOVB CL, (R10) + INCQ AX + INCQ R10 + DECQ R13 + JNZ copy_slow_3 handle_loop: MOVQ ctx+16(FP), AX @@ -2663,13 +2663,13 @@ copy_4_small: 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 + MOVWQZX (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 @@ -2729,13 +2729,13 @@ copy_5_move_1or2: 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 + MOVWQZX (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 @@ -2786,12 +2786,12 @@ 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 + MOVBQZX (CX), R12 + MOVB R12, (R9) + INCQ CX + INCQ R9 + DECQ R13 + JNZ copy_slow_3 handle_loop: MOVQ ctx+16(FP), CX @@ -2935,43 +2935,43 @@ sequenceDecs_decodeSync_safe_amd64_fill_check_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 + MOVQ R9, AX + MOVQ BX, CX + MOVQ DX, R14 + SHLQ CL, R14 + MOVBLZX AH, CX + 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 + MOVQ R8, AX + MOVQ BX, CX + MOVQ DX, R14 + SHLQ CL, R14 + MOVBLZX AH, CX + 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) @@ -3006,22 +3006,22 @@ sequenceDecs_decodeSync_safe_amd64_fill_2_check_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 + MOVQ DI, AX + MOVQ BX, CX + MOVQ DX, R14 + SHLQ CL, R14 + MOVBLZX AH, CX + 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) @@ -3043,7 +3043,7 @@ sequenceDecs_decodeSync_safe_amd64_ll_update_zero: MOVQ CX, BX ROLQ CL, R14 MOVL $0x00000001, R15 - MOVB R13, CL + MOVBLZX R13, CX SHLL CL, R15 DECL R15 ANDQ R15, R14 @@ -3062,7 +3062,7 @@ sequenceDecs_decodeSync_safe_amd64_ll_update_zero: MOVQ CX, BX ROLQ CL, R14 MOVL $0x00000001, R15 - MOVB R13, CL + MOVBLZX R13, CX SHLL CL, R15 DECL R15 ANDQ R15, R14 @@ -3081,7 +3081,7 @@ sequenceDecs_decodeSync_safe_amd64_ll_update_zero: MOVQ CX, BX ROLQ CL, R14 MOVL $0x00000001, R15 - MOVB R13, CL + MOVBLZX R13, CX SHLL CL, R15 DECL R15 ANDQ R15, R14 @@ -3206,13 +3206,13 @@ copy_1_move_1or2: 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 + MOVWQZX (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 @@ -3276,13 +3276,13 @@ copy_4_small: 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 + MOVWQZX (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 @@ -3342,13 +3342,13 @@ copy_5_move_1or2: 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 + MOVWQZX (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 @@ -3417,13 +3417,13 @@ copy_2_move_1or2: 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 + MOVWQZX (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 @@ -3450,12 +3450,12 @@ 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 + MOVBQZX (AX), CX + MOVB CL, (R10) + INCQ AX + INCQ R10 + DECQ R13 + JNZ copy_slow_3 handle_loop: MOVQ ctx+16(FP), AX @@ -3828,13 +3828,13 @@ copy_1_move_1or2: 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 + MOVWQZX (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 @@ -3898,13 +3898,13 @@ copy_4_small: 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 + MOVWQZX (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 @@ -3964,13 +3964,13 @@ copy_5_move_1or2: 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 + MOVWQZX (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 @@ -4039,13 +4039,13 @@ copy_2_move_1or2: 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 + MOVWQZX (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 @@ -4072,12 +4072,12 @@ 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 + MOVBQZX (CX), R12 + MOVB R12, (R9) + INCQ CX + INCQ R9 + DECQ R13 + JNZ copy_slow_3 handle_loop: MOVQ ctx+16(FP), CX diff --git a/vendor/github.com/klauspost/compress/zstd/seqdec_arm64.s b/vendor/github.com/klauspost/compress/zstd/seqdec_arm64.s index 6f54ef21ba..fa48be19e4 100644 --- a/vendor/github.com/klauspost/compress/zstd/seqdec_arm64.s +++ b/vendor/github.com/klauspost/compress/zstd/seqdec_arm64.s @@ -1,7 +1,7 @@ // Code generated by command: go run gen.go -out ../seqdec.s -arch amd64,arm64 -pkg=zstd. DO NOT EDIT. // EXPERIMENTAL arm64 output lowered from an amd64 avo program. -//go:build arm64 && !appengine && !noasm && gc && !noasm +//go:build arm64 && (!appengine && !noasm && gc && !noasm) // func sequenceDecs_decode_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int // Requires: CMOV @@ -60,8 +60,7 @@ sequenceDecs_decode_amd64_fill_end: MOVD R3, R1 MOVD R2, R14 LSL R1, R14, R14 - UBFX $8, R0, $8, R16 - BFI $0, R16, $8, R1 + UBFX $8, R0, $8, R1 LSR $0x20, R0, R0 TST R1, R1 BEQ sequenceDecs_decode_amd64_of_update_zero @@ -82,8 +81,7 @@ sequenceDecs_decode_amd64_of_update_zero: MOVD R3, R1 MOVD R2, R14 LSL R1, R14, R14 - UBFX $8, R0, $8, R16 - BFI $0, R16, $8, R1 + UBFX $8, R0, $8, R1 LSR $0x20, R0, R0 TST R1, R1 BEQ sequenceDecs_decode_amd64_ml_update_zero @@ -133,8 +131,7 @@ sequenceDecs_decode_amd64_fill_2_end: MOVD R3, R1 MOVD R2, R14 LSL R1, R14, R14 - UBFX $8, R0, $8, R16 - BFI $0, R16, $8, R1 + UBFX $8, R0, $8, R1 LSR $0x20, R0, R0 TST R1, R1 BEQ sequenceDecs_decode_amd64_ll_update_zero @@ -151,14 +148,12 @@ sequenceDecs_decode_amd64_ll_update_zero: MOVD R0, (R9) // Fill bitreader for state updates - MOVD R13, 8(RSP) - MOVD R8, R0 - LSR $0x08, R0, R0 - MOVBU R0, R0 - MOVD ctx+16(FP), R1 - MOVD 96(R1), R16 - CMP $0x00, R16 - BEQ sequenceDecs_decode_amd64_skip_update + MOVD R13, 8(RSP) + UBFX $8, R8, $8, R0 + MOVD ctx+16(FP), R1 + MOVD 96(R1), R16 + CMP $0x00, R16 + BEQ sequenceDecs_decode_amd64_skip_update // Update Literal Length State MOVBU R6, R13 @@ -169,7 +164,7 @@ sequenceDecs_decode_amd64_ll_update_zero: NEG R1, R16 ROR R16, R14, R14 MOVD $0x00000001, R4 - BFI $0, R13, $8, R1 + MOVBU R13, R1 LSLW R1, R4, R4 SUBW $1, R4, R4 AND R4, R14, R14 @@ -178,8 +173,7 @@ sequenceDecs_decode_amd64_ll_update_zero: // Load ctx.llTable MOVD ctx+16(FP), R1 MOVD (R1), R1 - ADD R6<<3, R1, R15 - MOVD (R15), R6 + MOVD (R1)(R6<<3), R6 // Update Match Length State MOVBU R7, R13 @@ -190,7 +184,7 @@ sequenceDecs_decode_amd64_ll_update_zero: NEG R1, R16 ROR R16, R14, R14 MOVD $0x00000001, R4 - BFI $0, R13, $8, R1 + MOVBU R13, R1 LSLW R1, R4, R4 SUBW $1, R4, R4 AND R4, R14, R14 @@ -199,8 +193,7 @@ sequenceDecs_decode_amd64_ll_update_zero: // Load ctx.mlTable MOVD ctx+16(FP), R1 MOVD 24(R1), R1 - ADD R7<<3, R1, R15 - MOVD (R15), R7 + MOVD (R1)(R7<<3), R7 // Update Offset State MOVBU R8, R13 @@ -211,7 +204,7 @@ sequenceDecs_decode_amd64_ll_update_zero: NEG R1, R16 ROR R16, R14, R14 MOVD $0x00000001, R4 - BFI $0, R13, $8, R1 + MOVBU R13, R1 LSLW R1, R4, R4 SUBW $1, R4, R4 AND R4, R14, R14 @@ -220,8 +213,7 @@ sequenceDecs_decode_amd64_ll_update_zero: // Load ctx.ofTable MOVD ctx+16(FP), R1 MOVD 48(R1), R1 - ADD R8<<3, R1, R15 - MOVD (R15), R8 + MOVD (R1)(R8<<3), R8 sequenceDecs_decode_amd64_skip_update: // Adjust offset @@ -411,8 +403,7 @@ sequenceDecs_decode_56_amd64_fill_end: MOVD R3, R1 MOVD R2, R14 LSL R1, R14, R14 - UBFX $8, R0, $8, R16 - BFI $0, R16, $8, R1 + UBFX $8, R0, $8, R1 LSR $0x20, R0, R0 TST R1, R1 BEQ sequenceDecs_decode_56_amd64_of_update_zero @@ -433,8 +424,7 @@ sequenceDecs_decode_56_amd64_of_update_zero: MOVD R3, R1 MOVD R2, R14 LSL R1, R14, R14 - UBFX $8, R0, $8, R16 - BFI $0, R16, $8, R1 + UBFX $8, R0, $8, R1 LSR $0x20, R0, R0 TST R1, R1 BEQ sequenceDecs_decode_56_amd64_ml_update_zero @@ -455,8 +445,7 @@ sequenceDecs_decode_56_amd64_ml_update_zero: MOVD R3, R1 MOVD R2, R14 LSL R1, R14, R14 - UBFX $8, R0, $8, R16 - BFI $0, R16, $8, R1 + UBFX $8, R0, $8, R1 LSR $0x20, R0, R0 TST R1, R1 BEQ sequenceDecs_decode_56_amd64_ll_update_zero @@ -473,14 +462,12 @@ sequenceDecs_decode_56_amd64_ll_update_zero: MOVD R0, (R9) // Fill bitreader for state updates - MOVD R13, 8(RSP) - MOVD R8, R0 - LSR $0x08, R0, R0 - MOVBU R0, R0 - MOVD ctx+16(FP), R1 - MOVD 96(R1), R16 - CMP $0x00, R16 - BEQ sequenceDecs_decode_56_amd64_skip_update + MOVD R13, 8(RSP) + UBFX $8, R8, $8, R0 + MOVD ctx+16(FP), R1 + MOVD 96(R1), R16 + CMP $0x00, R16 + BEQ sequenceDecs_decode_56_amd64_skip_update // Update Literal Length State MOVBU R6, R13 @@ -491,7 +478,7 @@ sequenceDecs_decode_56_amd64_ll_update_zero: NEG R1, R16 ROR R16, R14, R14 MOVD $0x00000001, R4 - BFI $0, R13, $8, R1 + MOVBU R13, R1 LSLW R1, R4, R4 SUBW $1, R4, R4 AND R4, R14, R14 @@ -500,8 +487,7 @@ sequenceDecs_decode_56_amd64_ll_update_zero: // Load ctx.llTable MOVD ctx+16(FP), R1 MOVD (R1), R1 - ADD R6<<3, R1, R15 - MOVD (R15), R6 + MOVD (R1)(R6<<3), R6 // Update Match Length State MOVBU R7, R13 @@ -512,7 +498,7 @@ sequenceDecs_decode_56_amd64_ll_update_zero: NEG R1, R16 ROR R16, R14, R14 MOVD $0x00000001, R4 - BFI $0, R13, $8, R1 + MOVBU R13, R1 LSLW R1, R4, R4 SUBW $1, R4, R4 AND R4, R14, R14 @@ -521,8 +507,7 @@ sequenceDecs_decode_56_amd64_ll_update_zero: // Load ctx.mlTable MOVD ctx+16(FP), R1 MOVD 24(R1), R1 - ADD R7<<3, R1, R15 - MOVD (R15), R7 + MOVD (R1)(R7<<3), R7 // Update Offset State MOVBU R8, R13 @@ -533,7 +518,7 @@ sequenceDecs_decode_56_amd64_ll_update_zero: NEG R1, R16 ROR R16, R14, R14 MOVD $0x00000001, R4 - BFI $0, R13, $8, R1 + MOVBU R13, R1 LSLW R1, R4, R4 SUBW $1, R4, R4 AND R4, R14, R14 @@ -542,8 +527,7 @@ sequenceDecs_decode_56_amd64_ll_update_zero: // Load ctx.ofTable MOVD ctx+16(FP), R1 MOVD 48(R1), R1 - ADD R8<<3, R1, R15 - MOVD (R15), R8 + MOVD (R1)(R8<<3), R8 sequenceDecs_decode_56_amd64_skip_update: // Adjust offset @@ -716,16 +700,16 @@ main_loop: MOVD $0, R13 copy_1: - ADD R13, R5, R15 - VLD1 (R15), [V0.B16] - ADD R13, R3, R15 - VST1 [V0.B16], (R15) - ADD $0x10, R13, R13 - CMP R10, R13 - BLO copy_1 - ADD R10, R5, R5 - ADD R10, R3, R3 - ADD R10, R6, R6 + ADD R13, R5, R15 + FMOVQ (R15), F0 + ADD R13, R3, R15 + FMOVQ F0, (R15) + ADD $0x10, R13, R13 + CMP R10, R13 + BLO copy_1 + ADD R10, R5, R5 + ADD R10, R3, R3 + ADD R10, R6, R6 // Malformed input if seq.mo > t+len(hist) || seq.mo > s.windowSize) check_offset: @@ -748,21 +732,19 @@ check_offset: BLO copy_4_small copy_4_loop: - VLD1 (R13), [V0.B16] - VST1 [V0.B16], (R3) - ADD $0x10, R13, R13 - ADD $0x10, R3, R3 - SUBS $0x10, R10, R10 - BHS copy_4_loop - ADD R10, R13, R13 - ADD $16, R13, R13 - ADD R10, R3, R3 - ADD $16, R3, R3 - ADD $-16, R13, R15 - VLD1 (R15), [V0.B16] - ADD $-16, R3, R15 - VST1 [V0.B16], (R15) - JMP copy_4_end + FMOVQ (R13), F0 + FMOVQ F0, (R3) + ADD $0x10, R13, R13 + ADD $0x10, R3, R3 + SUBS $0x10, R10, R10 + BHS copy_4_loop + ADD R10, R13, R13 + ADD $16, R13, R13 + ADD R10, R3, R3 + ADD $16, R3, R3 + FMOVQ -16(R13), F0 + FMOVQ F0, -16(R3) + JMP copy_4_end copy_4_small: CMP $0x03, R12 @@ -816,21 +798,19 @@ copy_all_from_history: BLO copy_5_small copy_5_loop: - VLD1 (R13), [V0.B16] - VST1 [V0.B16], (R3) - ADD $0x10, R13, R13 - ADD $0x10, R3, R3 - SUBS $0x10, R14, R14 - BHS copy_5_loop - ADD R14, R13, R13 - ADD $16, R13, R13 - ADD R14, R3, R3 - ADD $16, R3, R3 - ADD $-16, R13, R15 - VLD1 (R15), [V0.B16] - ADD $-16, R3, R15 - VST1 [V0.B16], (R15) - JMP copy_5_end + FMOVQ (R13), F0 + FMOVQ F0, (R3) + ADD $0x10, R13, R13 + ADD $0x10, R3, R3 + SUBS $0x10, R14, R14 + BHS copy_5_loop + ADD R14, R13, R13 + ADD $16, R13, R13 + ADD R14, R3, R3 + ADD $16, R3, R3 + FMOVQ -16(R13), F0 + FMOVQ F0, -16(R3) + JMP copy_5_end copy_5_small: CMP $0x03, R10 @@ -903,21 +883,20 @@ copy_match: ADD R12, R3, R3 copy_2: - VLD1 (R10), [V0.B16] - VST1 [V0.B16], (R11) - ADD $0x10, R10, R10 - ADD $0x10, R11, R11 - SUBS $0x10, R12, R12 - BHI copy_2 - JMP handle_loop + FMOVQ (R10), F0 + FMOVQ F0, (R11) + ADD $0x10, R10, R10 + ADD $0x10, R11, R11 + SUBS $0x10, R12, R12 + BHI copy_2 + JMP handle_loop // Copy overlapping match copy_overlapping_match: ADD R12, R6, R6 copy_slow_3: - MOVBU (R10), R16 - BFI $0, R16, $8, R11 + MOVBU (R10), R11 MOVB R11, (R3) ADD $1, R10, R10 ADD $1, R3, R3 @@ -1002,21 +981,19 @@ main_loop: BLO copy_1_small copy_1_loop: - VLD1 (R5), [V0.B16] - VST1 [V0.B16], (R3) - ADD $0x10, R5, R5 - ADD $0x10, R3, R3 - SUBS $0x10, R13, R13 - BHS copy_1_loop - ADD R13, R5, R5 - ADD $16, R5, R5 - ADD R13, R3, R3 - ADD $16, R3, R3 - ADD $-16, R5, R15 - VLD1 (R15), [V0.B16] - ADD $-16, R3, R15 - VST1 [V0.B16], (R15) - JMP copy_1_end + FMOVQ (R5), F0 + FMOVQ F0, (R3) + ADD $0x10, R5, R5 + ADD $0x10, R3, R3 + SUBS $0x10, R13, R13 + BHS copy_1_loop + ADD R13, R5, R5 + ADD $16, R5, R5 + ADD R13, R3, R3 + ADD $16, R3, R3 + FMOVQ -16(R5), F0 + FMOVQ F0, -16(R3) + JMP copy_1_end copy_1_small: CMP $0x03, R10 @@ -1094,21 +1071,19 @@ check_offset: BLO copy_4_small copy_4_loop: - VLD1 (R13), [V0.B16] - VST1 [V0.B16], (R3) - ADD $0x10, R13, R13 - ADD $0x10, R3, R3 - SUBS $0x10, R10, R10 - BHS copy_4_loop - ADD R10, R13, R13 - ADD $16, R13, R13 - ADD R10, R3, R3 - ADD $16, R3, R3 - ADD $-16, R13, R15 - VLD1 (R15), [V0.B16] - ADD $-16, R3, R15 - VST1 [V0.B16], (R15) - JMP copy_4_end + FMOVQ (R13), F0 + FMOVQ F0, (R3) + ADD $0x10, R13, R13 + ADD $0x10, R3, R3 + SUBS $0x10, R10, R10 + BHS copy_4_loop + ADD R10, R13, R13 + ADD $16, R13, R13 + ADD R10, R3, R3 + ADD $16, R3, R3 + FMOVQ -16(R13), F0 + FMOVQ F0, -16(R3) + JMP copy_4_end copy_4_small: CMP $0x03, R12 @@ -1162,21 +1137,19 @@ copy_all_from_history: BLO copy_5_small copy_5_loop: - VLD1 (R13), [V0.B16] - VST1 [V0.B16], (R3) - ADD $0x10, R13, R13 - ADD $0x10, R3, R3 - SUBS $0x10, R14, R14 - BHS copy_5_loop - ADD R14, R13, R13 - ADD $16, R13, R13 - ADD R14, R3, R3 - ADD $16, R3, R3 - ADD $-16, R13, R15 - VLD1 (R15), [V0.B16] - ADD $-16, R3, R15 - VST1 [V0.B16], (R15) - JMP copy_5_end + FMOVQ (R13), F0 + FMOVQ F0, (R3) + ADD $0x10, R13, R13 + ADD $0x10, R3, R3 + SUBS $0x10, R14, R14 + BHS copy_5_loop + ADD R14, R13, R13 + ADD $16, R13, R13 + ADD R14, R3, R3 + ADD $16, R3, R3 + FMOVQ -16(R13), F0 + FMOVQ F0, -16(R3) + JMP copy_5_end copy_5_small: CMP $0x03, R10 @@ -1250,21 +1223,19 @@ copy_match: BLO copy_2_small copy_2_loop: - VLD1 (R10), [V0.B16] - VST1 [V0.B16], (R3) - ADD $0x10, R10, R10 - ADD $0x10, R3, R3 - SUBS $0x10, R11, R11 - BHS copy_2_loop - ADD R11, R10, R10 - ADD $16, R10, R10 - ADD R11, R3, R3 - ADD $16, R3, R3 - ADD $-16, R10, R15 - VLD1 (R15), [V0.B16] - ADD $-16, R3, R15 - VST1 [V0.B16], (R15) - JMP copy_2_end + FMOVQ (R10), F0 + FMOVQ F0, (R3) + ADD $0x10, R10, R10 + ADD $0x10, R3, R3 + SUBS $0x10, R11, R11 + BHS copy_2_loop + ADD R11, R10, R10 + ADD $16, R10, R10 + ADD R11, R3, R3 + ADD $16, R3, R3 + FMOVQ -16(R10), F0 + FMOVQ F0, -16(R3) + JMP copy_2_end copy_2_small: CMP $0x03, R12 @@ -1326,8 +1297,7 @@ copy_overlapping_match: ADD R12, R6, R6 copy_slow_3: - MOVBU (R10), R16 - BFI $0, R16, $8, R11 + MOVBU (R10), R11 MOVB R11, (R3) ADD $1, R10, R10 ADD $1, R3, R3 @@ -1453,8 +1423,7 @@ sequenceDecs_decodeSync_amd64_fill_end: MOVD R3, R1 MOVD R2, R13 LSL R1, R13, R13 - UBFX $8, R0, $8, R16 - BFI $0, R16, $8, R1 + UBFX $8, R0, $8, R1 LSR $0x20, R0, R0 TST R1, R1 BEQ sequenceDecs_decodeSync_amd64_of_update_zero @@ -1475,8 +1444,7 @@ sequenceDecs_decodeSync_amd64_of_update_zero: MOVD R3, R1 MOVD R2, R13 LSL R1, R13, R13 - UBFX $8, R0, $8, R16 - BFI $0, R16, $8, R1 + UBFX $8, R0, $8, R1 LSR $0x20, R0, R0 TST R1, R1 BEQ sequenceDecs_decodeSync_amd64_ml_update_zero @@ -1526,8 +1494,7 @@ sequenceDecs_decodeSync_amd64_fill_2_end: MOVD R3, R1 MOVD R2, R13 LSL R1, R13, R13 - UBFX $8, R0, $8, R16 - BFI $0, R16, $8, R1 + UBFX $8, R0, $8, R1 LSR $0x20, R0, R0 TST R1, R1 BEQ sequenceDecs_decodeSync_amd64_ll_update_zero @@ -1544,14 +1511,12 @@ sequenceDecs_decodeSync_amd64_ll_update_zero: MOVD R0, 32(RSP) // Fill bitreader for state updates - MOVD R12, 8(RSP) - MOVD R8, R0 - LSR $0x08, R0, R0 - MOVBU R0, R0 - MOVD ctx+16(FP), R1 - MOVD 96(R1), R16 - CMP $0x00, R16 - BEQ sequenceDecs_decodeSync_amd64_skip_update + MOVD R12, 8(RSP) + UBFX $8, R8, $8, R0 + MOVD ctx+16(FP), R1 + MOVD 96(R1), R16 + CMP $0x00, R16 + BEQ sequenceDecs_decodeSync_amd64_skip_update // Update Literal Length State MOVBU R6, R12 @@ -1562,7 +1527,7 @@ sequenceDecs_decodeSync_amd64_ll_update_zero: NEG R1, R16 ROR R16, R13, R13 MOVD $0x00000001, R14 - BFI $0, R12, $8, R1 + MOVBU R12, R1 LSLW R1, R14, R14 SUBW $1, R14, R14 AND R14, R13, R13 @@ -1571,8 +1536,7 @@ sequenceDecs_decodeSync_amd64_ll_update_zero: // Load ctx.llTable MOVD ctx+16(FP), R1 MOVD (R1), R1 - ADD R6<<3, R1, R15 - MOVD (R15), R6 + MOVD (R1)(R6<<3), R6 // Update Match Length State MOVBU R7, R12 @@ -1583,7 +1547,7 @@ sequenceDecs_decodeSync_amd64_ll_update_zero: NEG R1, R16 ROR R16, R13, R13 MOVD $0x00000001, R14 - BFI $0, R12, $8, R1 + MOVBU R12, R1 LSLW R1, R14, R14 SUBW $1, R14, R14 AND R14, R13, R13 @@ -1592,8 +1556,7 @@ sequenceDecs_decodeSync_amd64_ll_update_zero: // Load ctx.mlTable MOVD ctx+16(FP), R1 MOVD 24(R1), R1 - ADD R7<<3, R1, R15 - MOVD (R15), R7 + MOVD (R1)(R7<<3), R7 // Update Offset State MOVBU R8, R12 @@ -1604,7 +1567,7 @@ sequenceDecs_decodeSync_amd64_ll_update_zero: NEG R1, R16 ROR R16, R13, R13 MOVD $0x00000001, R14 - BFI $0, R12, $8, R1 + MOVBU R12, R1 LSLW R1, R14, R14 SUBW $1, R14, R14 AND R14, R13, R13 @@ -1613,21 +1576,18 @@ sequenceDecs_decodeSync_amd64_ll_update_zero: // Load ctx.ofTable MOVD ctx+16(FP), R1 MOVD 48(R1), R1 - ADD R8<<3, R1, R15 - MOVD (R15), R8 + MOVD (R1)(R8<<3), R8 sequenceDecs_decodeSync_amd64_skip_update: // Adjust offset - MOVD s+0(FP), R1 - MOVD 16(RSP), R12 - CMP $0x01, R0 - BLS sequenceDecs_decodeSync_amd64_adjust_offsetB_1_or_0 - ADD $144, R1, R15 - VLD1 (R15), [V0.B16] - MOVD R12, 144(R1) - ADD $152, R1, R15 - VST1 [V0.B16], (R15) - JMP sequenceDecs_decodeSync_amd64_after_adjust + MOVD s+0(FP), R1 + MOVD 16(RSP), R12 + CMP $0x01, R0 + BLS sequenceDecs_decodeSync_amd64_adjust_offsetB_1_or_0 + FMOVQ 144(R1), F0 + MOVD R12, 144(R1) + FMOVQ F0, 152(R1) + JMP sequenceDecs_decodeSync_amd64_after_adjust sequenceDecs_decodeSync_amd64_adjust_offsetB_1_or_0: MOVD 32(RSP), R16 @@ -1709,16 +1669,16 @@ sequenceDecs_decodeSync_amd64_match_len_ofs_ok: MOVD $0, R13 copy_1: - ADD R13, R10, R15 - VLD1 (R15), [V0.B16] - ADD R13, R9, R15 - VST1 [V0.B16], (R15) - ADD $0x10, R13, R13 - CMP R0, R13 - BLO copy_1 - ADD R0, R10, R10 - ADD R0, R9, R9 - ADD R0, R11, R11 + ADD R13, R10, R15 + FMOVQ (R15), F0 + ADD R13, R9, R15 + FMOVQ F0, (R15) + ADD $0x10, R13, R13 + CMP R0, R13 + BLO copy_1 + ADD R0, R10, R10 + ADD R0, R9, R9 + ADD R0, R11, R11 // Malformed input if seq.mo > t+len(hist) || seq.mo > s.windowSize) check_offset: @@ -1744,21 +1704,19 @@ check_offset: BLO copy_4_small copy_4_loop: - VLD1 (R13), [V0.B16] - VST1 [V0.B16], (R9) - ADD $0x10, R13, R13 - ADD $0x10, R9, R9 - SUBS $0x10, R0, R0 - BHS copy_4_loop - ADD R0, R13, R13 - ADD $16, R13, R13 - ADD R0, R9, R9 - ADD $16, R9, R9 - ADD $-16, R13, R15 - VLD1 (R15), [V0.B16] - ADD $-16, R9, R15 - VST1 [V0.B16], (R15) - JMP copy_4_end + FMOVQ (R13), F0 + FMOVQ F0, (R9) + ADD $0x10, R13, R13 + ADD $0x10, R9, R9 + SUBS $0x10, R0, R0 + BHS copy_4_loop + ADD R0, R13, R13 + ADD $16, R13, R13 + ADD R0, R9, R9 + ADD $16, R9, R9 + FMOVQ -16(R13), F0 + FMOVQ F0, -16(R9) + JMP copy_4_end copy_4_small: CMP $0x03, R12 @@ -1809,21 +1767,19 @@ copy_all_from_history: BLO copy_5_small copy_5_loop: - VLD1 (R13), [V0.B16] - VST1 [V0.B16], (R9) - ADD $0x10, R13, R13 - ADD $0x10, R9, R9 - SUBS $0x10, R14, R14 - BHS copy_5_loop - ADD R14, R13, R13 - ADD $16, R13, R13 - ADD R14, R9, R9 - ADD $16, R9, R9 - ADD $-16, R13, R15 - VLD1 (R15), [V0.B16] - ADD $-16, R9, R15 - VST1 [V0.B16], (R15) - JMP copy_5_end + FMOVQ (R13), F0 + FMOVQ F0, (R9) + ADD $0x10, R13, R13 + ADD $0x10, R9, R9 + SUBS $0x10, R14, R14 + BHS copy_5_loop + ADD R14, R13, R13 + ADD $16, R13, R13 + ADD R14, R9, R9 + ADD $16, R9, R9 + FMOVQ -16(R13), F0 + FMOVQ F0, -16(R9) + JMP copy_5_end copy_5_small: CMP $0x03, R0 @@ -1896,21 +1852,20 @@ copy_match: ADD R12, R9, R9 copy_2: - VLD1 (R0), [V0.B16] - VST1 [V0.B16], (R1) - ADD $0x10, R0, R0 - ADD $0x10, R1, R1 - SUBS $0x10, R12, R12 - BHI copy_2 - JMP handle_loop + FMOVQ (R0), F0 + FMOVQ F0, (R1) + ADD $0x10, R0, R0 + ADD $0x10, R1, R1 + SUBS $0x10, R12, R12 + BHI copy_2 + JMP handle_loop // Copy overlapping match copy_overlapping_match: ADD R12, R11, R11 copy_slow_3: - MOVBU (R0), R16 - BFI $0, R16, $8, R1 + MOVBU (R0), R1 MOVB R1, (R9) ADD $1, R0, R0 ADD $1, R9, R9 @@ -2078,8 +2033,7 @@ sequenceDecs_decodeSync_safe_amd64_fill_end: MOVD R3, R1 MOVD R2, R13 LSL R1, R13, R13 - UBFX $8, R0, $8, R16 - BFI $0, R16, $8, R1 + UBFX $8, R0, $8, R1 LSR $0x20, R0, R0 TST R1, R1 BEQ sequenceDecs_decodeSync_safe_amd64_of_update_zero @@ -2100,8 +2054,7 @@ sequenceDecs_decodeSync_safe_amd64_of_update_zero: MOVD R3, R1 MOVD R2, R13 LSL R1, R13, R13 - UBFX $8, R0, $8, R16 - BFI $0, R16, $8, R1 + UBFX $8, R0, $8, R1 LSR $0x20, R0, R0 TST R1, R1 BEQ sequenceDecs_decodeSync_safe_amd64_ml_update_zero @@ -2151,8 +2104,7 @@ sequenceDecs_decodeSync_safe_amd64_fill_2_end: MOVD R3, R1 MOVD R2, R13 LSL R1, R13, R13 - UBFX $8, R0, $8, R16 - BFI $0, R16, $8, R1 + UBFX $8, R0, $8, R1 LSR $0x20, R0, R0 TST R1, R1 BEQ sequenceDecs_decodeSync_safe_amd64_ll_update_zero @@ -2169,14 +2121,12 @@ sequenceDecs_decodeSync_safe_amd64_ll_update_zero: MOVD R0, 32(RSP) // Fill bitreader for state updates - MOVD R12, 8(RSP) - MOVD R8, R0 - LSR $0x08, R0, R0 - MOVBU R0, R0 - MOVD ctx+16(FP), R1 - MOVD 96(R1), R16 - CMP $0x00, R16 - BEQ sequenceDecs_decodeSync_safe_amd64_skip_update + MOVD R12, 8(RSP) + UBFX $8, R8, $8, R0 + MOVD ctx+16(FP), R1 + MOVD 96(R1), R16 + CMP $0x00, R16 + BEQ sequenceDecs_decodeSync_safe_amd64_skip_update // Update Literal Length State MOVBU R6, R12 @@ -2187,7 +2137,7 @@ sequenceDecs_decodeSync_safe_amd64_ll_update_zero: NEG R1, R16 ROR R16, R13, R13 MOVD $0x00000001, R14 - BFI $0, R12, $8, R1 + MOVBU R12, R1 LSLW R1, R14, R14 SUBW $1, R14, R14 AND R14, R13, R13 @@ -2196,8 +2146,7 @@ sequenceDecs_decodeSync_safe_amd64_ll_update_zero: // Load ctx.llTable MOVD ctx+16(FP), R1 MOVD (R1), R1 - ADD R6<<3, R1, R15 - MOVD (R15), R6 + MOVD (R1)(R6<<3), R6 // Update Match Length State MOVBU R7, R12 @@ -2208,7 +2157,7 @@ sequenceDecs_decodeSync_safe_amd64_ll_update_zero: NEG R1, R16 ROR R16, R13, R13 MOVD $0x00000001, R14 - BFI $0, R12, $8, R1 + MOVBU R12, R1 LSLW R1, R14, R14 SUBW $1, R14, R14 AND R14, R13, R13 @@ -2217,8 +2166,7 @@ sequenceDecs_decodeSync_safe_amd64_ll_update_zero: // Load ctx.mlTable MOVD ctx+16(FP), R1 MOVD 24(R1), R1 - ADD R7<<3, R1, R15 - MOVD (R15), R7 + MOVD (R1)(R7<<3), R7 // Update Offset State MOVBU R8, R12 @@ -2229,7 +2177,7 @@ sequenceDecs_decodeSync_safe_amd64_ll_update_zero: NEG R1, R16 ROR R16, R13, R13 MOVD $0x00000001, R14 - BFI $0, R12, $8, R1 + MOVBU R12, R1 LSLW R1, R14, R14 SUBW $1, R14, R14 AND R14, R13, R13 @@ -2238,21 +2186,18 @@ sequenceDecs_decodeSync_safe_amd64_ll_update_zero: // Load ctx.ofTable MOVD ctx+16(FP), R1 MOVD 48(R1), R1 - ADD R8<<3, R1, R15 - MOVD (R15), R8 + MOVD (R1)(R8<<3), R8 sequenceDecs_decodeSync_safe_amd64_skip_update: // Adjust offset - MOVD s+0(FP), R1 - MOVD 16(RSP), R12 - CMP $0x01, R0 - BLS sequenceDecs_decodeSync_safe_amd64_adjust_offsetB_1_or_0 - ADD $144, R1, R15 - VLD1 (R15), [V0.B16] - MOVD R12, 144(R1) - ADD $152, R1, R15 - VST1 [V0.B16], (R15) - JMP sequenceDecs_decodeSync_safe_amd64_after_adjust + MOVD s+0(FP), R1 + MOVD 16(RSP), R12 + CMP $0x01, R0 + BLS sequenceDecs_decodeSync_safe_amd64_adjust_offsetB_1_or_0 + FMOVQ 144(R1), F0 + MOVD R12, 144(R1) + FMOVQ F0, 152(R1) + JMP sequenceDecs_decodeSync_safe_amd64_after_adjust sequenceDecs_decodeSync_safe_amd64_adjust_offsetB_1_or_0: MOVD 32(RSP), R16 @@ -2335,21 +2280,19 @@ sequenceDecs_decodeSync_safe_amd64_match_len_ofs_ok: BLO copy_1_small copy_1_loop: - VLD1 (R10), [V0.B16] - VST1 [V0.B16], (R9) - ADD $0x10, R10, R10 - ADD $0x10, R9, R9 - SUBS $0x10, R13, R13 - BHS copy_1_loop - ADD R13, R10, R10 - ADD $16, R10, R10 - ADD R13, R9, R9 - ADD $16, R9, R9 - ADD $-16, R10, R15 - VLD1 (R15), [V0.B16] - ADD $-16, R9, R15 - VST1 [V0.B16], (R15) - JMP copy_1_end + FMOVQ (R10), F0 + FMOVQ F0, (R9) + ADD $0x10, R10, R10 + ADD $0x10, R9, R9 + SUBS $0x10, R13, R13 + BHS copy_1_loop + ADD R13, R10, R10 + ADD $16, R10, R10 + ADD R13, R9, R9 + ADD $16, R9, R9 + FMOVQ -16(R10), F0 + FMOVQ F0, -16(R9) + JMP copy_1_end copy_1_small: CMP $0x03, R0 @@ -2430,21 +2373,19 @@ check_offset: BLO copy_4_small copy_4_loop: - VLD1 (R13), [V0.B16] - VST1 [V0.B16], (R9) - ADD $0x10, R13, R13 - ADD $0x10, R9, R9 - SUBS $0x10, R0, R0 - BHS copy_4_loop - ADD R0, R13, R13 - ADD $16, R13, R13 - ADD R0, R9, R9 - ADD $16, R9, R9 - ADD $-16, R13, R15 - VLD1 (R15), [V0.B16] - ADD $-16, R9, R15 - VST1 [V0.B16], (R15) - JMP copy_4_end + FMOVQ (R13), F0 + FMOVQ F0, (R9) + ADD $0x10, R13, R13 + ADD $0x10, R9, R9 + SUBS $0x10, R0, R0 + BHS copy_4_loop + ADD R0, R13, R13 + ADD $16, R13, R13 + ADD R0, R9, R9 + ADD $16, R9, R9 + FMOVQ -16(R13), F0 + FMOVQ F0, -16(R9) + JMP copy_4_end copy_4_small: CMP $0x03, R12 @@ -2495,21 +2436,19 @@ copy_all_from_history: BLO copy_5_small copy_5_loop: - VLD1 (R13), [V0.B16] - VST1 [V0.B16], (R9) - ADD $0x10, R13, R13 - ADD $0x10, R9, R9 - SUBS $0x10, R14, R14 - BHS copy_5_loop - ADD R14, R13, R13 - ADD $16, R13, R13 - ADD R14, R9, R9 - ADD $16, R9, R9 - ADD $-16, R13, R15 - VLD1 (R15), [V0.B16] - ADD $-16, R9, R15 - VST1 [V0.B16], (R15) - JMP copy_5_end + FMOVQ (R13), F0 + FMOVQ F0, (R9) + ADD $0x10, R13, R13 + ADD $0x10, R9, R9 + SUBS $0x10, R14, R14 + BHS copy_5_loop + ADD R14, R13, R13 + ADD $16, R13, R13 + ADD R14, R9, R9 + ADD $16, R9, R9 + FMOVQ -16(R13), F0 + FMOVQ F0, -16(R9) + JMP copy_5_end copy_5_small: CMP $0x03, R0 @@ -2583,21 +2522,19 @@ copy_match: BLO copy_2_small copy_2_loop: - VLD1 (R0), [V0.B16] - VST1 [V0.B16], (R9) - ADD $0x10, R0, R0 - ADD $0x10, R9, R9 - SUBS $0x10, R1, R1 - BHS copy_2_loop - ADD R1, R0, R0 - ADD $16, R0, R0 - ADD R1, R9, R9 - ADD $16, R9, R9 - ADD $-16, R0, R15 - VLD1 (R15), [V0.B16] - ADD $-16, R9, R15 - VST1 [V0.B16], (R15) - JMP copy_2_end + FMOVQ (R0), F0 + FMOVQ F0, (R9) + ADD $0x10, R0, R0 + ADD $0x10, R9, R9 + SUBS $0x10, R1, R1 + BHS copy_2_loop + ADD R1, R0, R0 + ADD $16, R0, R0 + ADD R1, R9, R9 + ADD $16, R9, R9 + FMOVQ -16(R0), F0 + FMOVQ F0, -16(R9) + JMP copy_2_end copy_2_small: CMP $0x03, R12 @@ -2659,8 +2596,7 @@ copy_overlapping_match: ADD R12, R11, R11 copy_slow_3: - MOVBU (R0), R16 - BFI $0, R16, $8, R1 + MOVBU (R0), R1 MOVB R1, (R9) ADD $1, R0, R0 ADD $1, R9, R9 diff --git a/vendor/github.com/lestrrat-go/dsig/.golangci.yml b/vendor/github.com/lestrrat-go/dsig/.golangci.yml new file mode 100644 index 0000000000..8cde331d8e --- /dev/null +++ b/vendor/github.com/lestrrat-go/dsig/.golangci.yml @@ -0,0 +1,14 @@ +version: "2" + +# The linter set is golangci-lint's default. The point of this file is the +# formatters block below: without it nothing checked gofmt, and the drift that +# allowed put a closing code fence on the same line as a line of Go, which +# broke half of README.md once the doc generator started running. +formatters: + enable: + - gofmt + +issues: + # Report every unformatted file. The default caps repeats of one message at + # three, which would hide the tail of exactly this kind of sweep. + max-same-issues: 0 diff --git a/vendor/github.com/lestrrat-go/dsig/Changes b/vendor/github.com/lestrrat-go/dsig/Changes index 5e7a522cd1..7264f1801d 100644 --- a/vendor/github.com/lestrrat-go/dsig/Changes +++ b/vendor/github.com/lestrrat-go/dsig/Changes @@ -1,6 +1,57 @@ Changes ======= +v1.4.0 20 Aug 2026 + * Add ML-DSA (FIPS 204) support: the `MLDSA44`, `MLDSA65`, and `MLDSA87` + algorithms, the `SignMLDSA()` / `VerifyMLDSA()` primitives, and a new + `MLDSAFamily` algorithm family. ML-DSA requires Go 1.27 or later, which is + when `crypto/mldsa` joins the standard library; on earlier toolchains the + constants are not declared and the algorithms are not registered. + + An ML-DSA key carries its own parameter set, so naming an algorithm that + disagrees with the key is an error on both the sign and verify paths. + `SignMLDSA()` takes a `crypto.SignerOpts` so that both signing modes stay + reachable: an `*mldsa.Options` supplies a context string, and + `crypto.MLDSAMu` signs a pre-hashed mu message representative. + `VerifyMLDSA()` takes an `*mldsa.Options` because verification has a single + mode. `SignDigest()` and `VerifyDigest()` return an error for ML-DSA, as + they already do for EdDSA. + + * The minimum Go version is now 1.25. + +v1.3.0 13 Apr 2026 + * Add `SignWithOpts()` and `VerifyWithOpts()`, which thread an optional + `crypto.SignerOpts` through to the underlying signer. For built-in + families (HMAC, RSA, ECDSA, EdDSA) the opts argument is ignored. For + the `Custom` family, opts are forwarded to the algorithm's Meta when + it implements the new `SignerWithOpts` / `VerifierWithOpts` interfaces; + otherwise the dispatcher falls back to the plain `Signer` / `Verifier` + methods and the opts are dropped. The canonical use case is composite + ML-DSA signatures, where a per-call domain-separation context + (`*mldsa.Options`) must reach `filippo.io/mldsa`. + + * `Sign()` is now a one-line wrapper around `SignWithOpts()` (and + `Verify()` likewise wraps `VerifyWithOpts()`). The public signatures + of `Sign` and `Verify` are unchanged; the only observable difference + for existing callers is one extra call frame. + + * `RegisterAlgorithm()` for the `Custom` family now accepts a Meta that + implements only `SignerWithOpts` / `VerifierWithOpts` (in addition to + the existing `Signer` / `Verifier` paths). + + * In dsig v2, `Sign` / `Verify` will absorb the opts parameter and + `SignWithOpts` / `VerifyWithOpts` will be removed. The same migration + is planned for `SignDigest` / `VerifyDigest` once a `DigestSigner` + interface for the `Custom` family lands. Doc comments on all four + entry points flag the upcoming change. + +v1.2.2 13 Apr 2026 + * Add `SignECDSADER()` and `VerifyECDSADER()` primitive helpers for ECDSA + signatures in ASN.1 DER-encoded `Ecdsa-Sig-Value` form (RFC 3279 §2.2.3), + as used by X.509/PKIX and composite signature schemes. The existing + `SignECDSA()`/`VerifyECDSA()` functions remain the canonical entry points + for the JWS-native fixed-length r||s format (RFC 7515 §3.4). + v1.2.1 7 Apr 2026 * Add `SignDigest()` for signing pre-computed digests. Supported for HMAC, RSA (PKCS1v15 and PSS), and ECDSA families. EdDSA and Custom return an error. @@ -25,4 +76,4 @@ v1.1.0 2 Apr 2026 algorithm name. Use `UnregisterAlgorithm()` first if you need to replace it. v1.0.0 - 18 Aug 2025 - * Initial release \ No newline at end of file + * Initial release diff --git a/vendor/github.com/lestrrat-go/dsig/README.md b/vendor/github.com/lestrrat-go/dsig/README.md index b52b998f8f..55b75963e9 100644 --- a/vendor/github.com/lestrrat-go/dsig/README.md +++ b/vendor/github.com/lestrrat-go/dsig/README.md @@ -1,4 +1,4 @@ -# github.com/lestrrat-go/dsig [![CI](https://github.com/lestrrat-go/dsig/actions/workflows/ci.yml/badge.svg)](https://github.com/lestrrat-go/dsig/actions/workflows/ci.yml) [![Go Reference](https://pkg.go.dev/badge/github.com/lestrrat-go/dsig.svg)](https://pkg.go.dev/github.com/lestrrat-go/dsig) [![codecov.io](https://codecov.io/github/lestrrat-go/dsig/coverage.svg?branch=v1)](https://codecov.io/github/lestrrat-go/dsig?branch=v1) +# github.com/lestrrat-go/dsig [![CI](https://github.com/lestrrat-go/dsig/actions/workflows/ci.yml/badge.svg)](https://github.com/lestrrat-go/dsig/actions/workflows/ci.yml) [![Go Reference](https://pkg.go.dev/badge/github.com/lestrrat-go/dsig.svg)](https://pkg.go.dev/github.com/lestrrat-go/dsig) Go module providing low-level digital signature operations. @@ -9,6 +9,7 @@ While there are many standards for generating and verifying digital signatures, * RSA signatures (PKCS1v15 and PSS) * ECDSA signatures (P-256, P-384, P-521) * EdDSA signatures (Ed25519, Ed448) +* ML-DSA post-quantum signatures (ML-DSA-44, ML-DSA-65, ML-DSA-87), on Go 1.27 and later * HMAC signatures (SHA-256, SHA-384, SHA-512) * Support for crypto.Signer interface * Custom algorithm registration via `Signer`/`Verifier` interfaces @@ -143,6 +144,53 @@ source: [examples/dsig_readme_example_test.go](https://github.com/lestrrat-go/ds | `ECDSAWithP384AndSHA384` | ECDSA using P-384 and SHA-384 | *ecdsa.PrivateKey / *ecdsa.PublicKey | | `ECDSAWithP521AndSHA512` | ECDSA using P-521 and SHA-512 | *ecdsa.PrivateKey / *ecdsa.PublicKey | | `EdDSA` | EdDSA using Ed25519 or Ed448 | ed25519.PrivateKey / ed25519.PublicKey | +| `MLDSA44` | ML-DSA-44 (FIPS 204), NIST level 2 | *mldsa.PrivateKey / *mldsa.PublicKey | +| `MLDSA65` | ML-DSA-65 (FIPS 204), NIST level 3 | *mldsa.PrivateKey / *mldsa.PublicKey | +| `MLDSA87` | ML-DSA-87 (FIPS 204), NIST level 5 | *mldsa.PrivateKey / *mldsa.PublicKey | + +The three ML-DSA algorithms need Go 1.27 or later, which is when `crypto/mldsa` +joins the standard library. On earlier toolchains the constants are not declared +and the algorithms are not registered. + +Name the constant that matches the key you generated. A key knows its own +parameter set, and naming a different one is an error, so a key cannot be used +under a weaker set by accident: + +```go +sk, _ := mldsa.GenerateKey(mldsa.MLDSA65()) + +sig, _ := dsig.Sign(sk, dsig.MLDSA65, payload, nil) +err := dsig.Verify(sk.PublicKey(), dsig.MLDSA65, payload, sig) + +_, err = dsig.Sign(sk, dsig.MLDSA44, payload, nil) +// dsig.SignWithOpts: ML-DSA parameter set mismatch: key is ML-DSA-65, algorithm is ML-DSA-44 +``` + +## ML-DSA context strings + +ML-DSA can mix a caller-chosen string into the signature. Give each job a +different context and one key can sign for several of them without a signature +made for one job verifying as another, so a login token cannot be presented as +a file receipt. + +Signing and verifying must use the same context. A verifier that supplies the +wrong one, or none at all, sees an ordinary invalid signature and cannot tell +which mistake was made: + +```go +login := &mldsa.Options{Context: "my-app/login-token"} +receipt := &mldsa.Options{Context: "my-app/file-receipt"} + +sig, _ := dsig.SignWithOpts(sk, dsig.MLDSA65, payload, login, nil) + +err := dsig.VerifyWithOpts(sk.PublicKey(), dsig.MLDSA65, payload, sig, login) +// nil + +err = dsig.VerifyWithOpts(sk.PublicKey(), dsig.MLDSA65, payload, sig, receipt) +// mldsa: invalid signature +``` + +A context is at most 255 bytes, and it is empty when opts is nil. # Description @@ -160,4 +208,4 @@ Please include tests that exercise your changes. # Related Libraries -* [github.com/lestrrat-go/jwx](https://github.com/lestrrat-go/jwx) - JOSE (JWA/JWE/JWK/JWS/JWT) implementation \ No newline at end of file +* [github.com/lestrrat-go/jwx](https://github.com/lestrrat-go/jwx) - JOSE (JWA/JWE/JWK/JWS/JWT) implementation diff --git a/vendor/github.com/lestrrat-go/dsig/algorithms.go b/vendor/github.com/lestrrat-go/dsig/algorithms.go index 0895c64764..3cf93b0c7e 100644 --- a/vendor/github.com/lestrrat-go/dsig/algorithms.go +++ b/vendor/github.com/lestrrat-go/dsig/algorithms.go @@ -34,4 +34,4 @@ const ( // EdDSA signature algorithms // These use Edwards-curve Digital Signature Algorithm (supports Ed25519 and Ed448) EdDSA = "EDDSA" -) \ No newline at end of file +) diff --git a/vendor/github.com/lestrrat-go/dsig/dsig.go b/vendor/github.com/lestrrat-go/dsig/dsig.go index a6b54418a7..ee278311be 100644 --- a/vendor/github.com/lestrrat-go/dsig/dsig.go +++ b/vendor/github.com/lestrrat-go/dsig/dsig.go @@ -28,6 +28,12 @@ const ( ECDSA EdDSAFamily Custom + // MLDSAFamily covers the ML-DSA parameter sets. It is deliberately not + // Custom: Custom means this library knows nothing about the algorithm, + // which would be false here and misleads callers that switch on Family. + // + // It sits after Custom so the values earlier releases assigned stay put. + MLDSAFamily maxFamily ) @@ -44,6 +50,8 @@ func (f Family) String() string { return "EdDSA" case Custom: return "Custom" + case MLDSAFamily: + return "ML-DSA" default: return "InvalidFamily" } @@ -85,6 +93,22 @@ type Signer interface { Sign(key any, payload []byte, rand io.Reader) ([]byte, error) } +// SignerWithOpts is an optional interface that Custom-family signers +// can implement to receive a per-call [crypto.SignerOpts]. The +// canonical use case is ML-DSA, whose Sign method accepts an +// *mldsa.Options carrying a domain-separation context that the plain +// [Signer] interface cannot convey. Custom Meta values that do not +// implement this interface still work with [SignWithOpts]: the +// dispatcher falls back to the plain [Signer.Sign] method and the opts +// argument is dropped. +// +// Implementing both [Signer] and SignerWithOpts is supported, but +// implementing only SignerWithOpts is sufficient because the dispatcher +// checks for it first. +type SignerWithOpts interface { + SignWithOpts(key any, payload []byte, opts crypto.SignerOpts, rand io.Reader) ([]byte, error) +} + // Verifier is an interface for custom verification implementations. // For the Custom algorithm family, info.Meta must implement this interface // to support verification. The implementation struct can carry any additional @@ -93,6 +117,12 @@ type Verifier interface { Verify(key any, payload, signature []byte) error } +// VerifierWithOpts is the verification counterpart of [SignerWithOpts]. +// See [SignerWithOpts] for usage notes. +type VerifierWithOpts interface { + VerifyWithOpts(key any, payload, signature []byte, opts crypto.SignerOpts) error +} + var algorithms = make(map[string]AlgorithmInfo) var builtinAlgorithms = make(map[string]struct{}) var muAlgorithms sync.RWMutex @@ -102,7 +132,8 @@ var muAlgorithms sync.RWMutex // info.Meta should contain extra metadata for some algorithms. HMAC, RSA, and ECDSA // families need their respective metadata (HMACFamilyMeta, RSAFamilyMeta, and // ECDSAFamilyMeta). Metadata for EdDSA is optional. For the Custom family, Meta -// must implement at least one of the Signer or Verifier interfaces. +// must implement at least one of the Signer, SignerWithOpts, Verifier, or +// VerifierWithOpts interfaces. // // Re-registration of an already-registered algorithm name is rejected. Use // UnregisterAlgorithm to remove it first if you need to replace it. @@ -130,11 +161,17 @@ func RegisterAlgorithm(name string, info AlgorithmInfo) error { } case EdDSAFamily: // EdDSA metadata is optional for now - case Custom: + case Custom, MLDSAFamily: + // Both families carry their implementation in Meta. The other families + // put passive metadata there. For ML-DSA this is forced: crypto/mldsa + // exists only from Go 1.27, so the algorithm cannot be described by a + // value type this file could name. _, isSigner := info.Meta.(Signer) + _, isSignerWithOpts := info.Meta.(SignerWithOpts) _, isVerifier := info.Meta.(Verifier) - if !isSigner && !isVerifier { - return fmt.Errorf("custom algorithm %s: Meta must implement Signer and/or Verifier", name) + _, isVerifierWithOpts := info.Meta.(VerifierWithOpts) + if !isSigner && !isSignerWithOpts && !isVerifier && !isVerifierWithOpts { + return fmt.Errorf("%s algorithm %s: Meta must implement Signer, SignerWithOpts, Verifier, or VerifierWithOpts", info.Family, name) } default: return fmt.Errorf("unsupported algorithm family %s for algorithm %s", info.Family, name) @@ -272,4 +309,3 @@ func init() { builtinAlgorithms[name] = struct{}{} } } - diff --git a/vendor/github.com/lestrrat-go/dsig/ecdsa.go b/vendor/github.com/lestrrat-go/dsig/ecdsa.go index 4041d9c53d..9da4ac244e 100644 --- a/vendor/github.com/lestrrat-go/dsig/ecdsa.go +++ b/vendor/github.com/lestrrat-go/dsig/ecdsa.go @@ -12,7 +12,6 @@ import ( "github.com/lestrrat-go/dsig/internal/ecutil" ) - func ecdsaGetSignerKey(key any) (*ecdsa.PrivateKey, crypto.Signer, bool, error) { cs, isCryptoSigner := key.(crypto.Signer) if isCryptoSigner { @@ -124,6 +123,47 @@ func SignECDSA(key *ecdsa.PrivateKey, payload []byte, h crypto.Hash, rr io.Reade return PackECDSASignature(r, s, key.Curve.Params().BitSize) } +// SignECDSADER generates an ECDSA signature in ASN.1 DER-encoded Ecdsa-Sig-Value +// format (RFC 3279 §2.2.3), as required by X.509/PKIX and composite signature +// schemes such as draft-ietf-lamps-pq-composite-sigs. For the fixed-length +// JWS r||s format (RFC 7515 §3.4), use SignECDSA instead. +// +// The payload is hashed with h before signing. rr provides randomness; if nil, +// rand.Reader is used. +func SignECDSADER(key *ecdsa.PrivateKey, payload []byte, h crypto.Hash, rr io.Reader) ([]byte, error) { + if !isValidECDSAKey(key) { + return nil, fmt.Errorf(`invalid key type %T for ECDSA algorithm`, key) + } + hh := h.New() + if _, err := hh.Write(payload); err != nil { + return nil, fmt.Errorf(`failed to write payload using ecdsa: %w`, err) + } + digest := hh.Sum(nil) + + if rr == nil { + rr = rand.Reader + } + + sig, err := ecdsa.SignASN1(rr, key, digest) + if err != nil { + return nil, fmt.Errorf(`failed to sign payload using ecdsa: %w`, err) + } + return sig, nil +} + +// VerifyECDSADER verifies an ECDSA signature in ASN.1 DER-encoded +// Ecdsa-Sig-Value format. See SignECDSADER for the format distinction. The +// payload is hashed with h before verification. +func VerifyECDSADER(key *ecdsa.PublicKey, payload, signature []byte, h crypto.Hash) error { + hh := h.New() + hh.Write(payload) + digest := hh.Sum(nil) + if !ecdsa.VerifyASN1(key, digest, signature) { + return NewVerificationError("invalid ECDSA signature") + } + return nil +} + // SignECDSACryptoSigner generates an ECDSA signature using a crypto.Signer interface. // This function works with hardware security modules and other crypto.Signer implementations. // The signature is converted from ASN.1 format to JWS format (r||s). diff --git a/vendor/github.com/lestrrat-go/dsig/mldsa.go b/vendor/github.com/lestrrat-go/dsig/mldsa.go new file mode 100644 index 0000000000..218204774d --- /dev/null +++ b/vendor/github.com/lestrrat-go/dsig/mldsa.go @@ -0,0 +1,193 @@ +//go:build go1.27 + +package dsig + +import ( + "crypto" + "crypto/mldsa" + "fmt" + "io" +) + +// ML-DSA signature algorithms, the post-quantum scheme specified in FIPS 204. +// The three names identify the three parameter sets, which differ in security +// level and in key and signature sizes. +// +// These names match what crypto/mldsa's Parameters.String reports, so the +// parameter set a key carries can be compared against the algorithm name +// directly. +// +// ML-DSA is available only when dsig is built with Go 1.27 or later, which is +// when crypto/mldsa becomes part of the standard library. On earlier +// toolchains these algorithms are not registered and not declared. +const ( + MLDSA44 = "ML-DSA-44" + MLDSA65 = "ML-DSA-65" + MLDSA87 = "ML-DSA-87" +) + +func init() { + for _, params := range []mldsa.Parameters{mldsa.MLDSA44(), mldsa.MLDSA65(), mldsa.MLDSA87()} { + name := params.String() + if err := RegisterAlgorithm(name, AlgorithmInfo{ + Family: MLDSAFamily, + Meta: &mldsaAlgorithm{params: params}, + }); err != nil { + panic(fmt.Sprintf("failed to register algorithm %s: %v", name, err)) + } + builtinAlgorithms[name] = struct{}{} + } +} + +// SignMLDSA generates an ML-DSA signature for the given payload. +// +// opts may be nil, which signs payload directly with no context. Pass an +// *[mldsa.Options] to supply a domain-separation context, which [VerifyMLDSA] +// then requires to match. +// +// opts is a [crypto.SignerOpts] so that both of ML-DSA's signing modes stay +// expressible. Passing [crypto.MLDSAMu] means payload holds a pre-hashed μ +// message representative. That mode is a shortcut for callers who already have +// μ, and it produces an ordinary signature; [VerifyMLDSA] checks it against the +// original message, and the verify side needs no counterpart. +// +// crypto/mldsa rejects any other opts value, so a mistaken type cannot be +// silently downgraded to a context-free signature. +func SignMLDSA(key *mldsa.PrivateKey, payload []byte, opts crypto.SignerOpts) ([]byte, error) { + if key == nil { + return nil, fmt.Errorf(`dsig.SignMLDSA: key cannot be nil`) + } + // The io.Reader argument is ignored by crypto/mldsa; signing draws its own + // randomness. SignDeterministic is the variant that draws none. + return key.Sign(nil, payload, opts) +} + +// VerifyMLDSA verifies an ML-DSA signature for the given payload. +// +// opts may be nil. It must carry the same Context that was used to produce the +// signature, otherwise verification fails. +// +// Verification has a single mode, so opts is a concrete *[mldsa.Options]. μ is +// derived from the message, so a signature made from a pre-hashed μ verifies +// here against the original message. +func VerifyMLDSA(key *mldsa.PublicKey, payload, signature []byte, opts *mldsa.Options) error { + if key == nil { + return fmt.Errorf(`dsig.VerifyMLDSA: key cannot be nil`) + } + return mldsa.Verify(key, payload, signature, opts) +} + +// mldsaAlgorithm is the Custom-family adapter that binds one ML-DSA parameter +// set to the registry. It carries the parameter set so that every operation can +// check the caller's key against the algorithm that was asked for. +type mldsaAlgorithm struct { + params mldsa.Parameters +} + +// requireMLDSAParams reports whether a caller-supplied key belongs to the +// parameter set this algorithm was registered for. crypto/mldsa's Parameters is +// a comparable value naming one of the three FIPS 204 sets, so a plain +// comparison suffices. +// +// The check matters because the key owns the parameter set, and the call only +// names one. Without it, an ML-DSA-65 key would happily produce and verify +// ML-DSA-65 signatures while the caller believed it had selected ML-DSA-44. +// Anything that reads the algorithm name to decide a post-quantum security +// level would then be misled, so the mismatch is an error. +func (a *mldsaAlgorithm) requireMLDSAParams(got mldsa.Parameters) error { + if got != a.params { + return fmt.Errorf(`ML-DSA parameter set mismatch: key is %s, algorithm is %s`, got, a.params) + } + return nil +} + +func (a *mldsaAlgorithm) privateKey(key any) (*mldsa.PrivateKey, error) { + sk, ok := key.(*mldsa.PrivateKey) + if !ok { + return nil, fmt.Errorf(`expected *mldsa.PrivateKey, got %T`, key) + } + if err := a.requireMLDSAParams(sk.PublicKey().Parameters()); err != nil { + return nil, err + } + return sk, nil +} + +// publicKey narrows the key types the verify surface accepts. A private key is +// allowed so callers holding only one half do not have to unwrap it themselves. +func (a *mldsaAlgorithm) publicKey(key any) (*mldsa.PublicKey, error) { + var pk *mldsa.PublicKey + switch k := key.(type) { + case *mldsa.PublicKey: + pk = k + case *mldsa.PrivateKey: + pk = k.PublicKey() + default: + return nil, fmt.Errorf(`expected *mldsa.PublicKey or *mldsa.PrivateKey, got %T`, key) + } + if err := a.requireMLDSAParams(pk.Parameters()); err != nil { + return nil, err + } + return pk, nil +} + +// mldsaOptions narrows a crypto.SignerOpts to the concrete type crypto/mldsa +// accepts. A non-nil value of any other type is an error. Dropping it would let +// a caller believe their Context was in force while the operation actually ran +// with an empty context, which is a signature substitution vector for schemes +// that rely on domain separation. +func mldsaOptions(opts crypto.SignerOpts) (*mldsa.Options, error) { + if opts == nil { + return nil, nil + } + mldsaOpts, ok := opts.(*mldsa.Options) + if !ok { + return nil, fmt.Errorf(`expected *mldsa.Options, got %T`, opts) + } + return mldsaOpts, nil +} + +func (a *mldsaAlgorithm) Sign(key any, payload []byte, _ io.Reader) ([]byte, error) { + sk, err := a.privateKey(key) + if err != nil { + return nil, fmt.Errorf(`dsig.Sign: %w`, err) + } + return SignMLDSA(sk, payload, nil) +} + +// SignWithOpts implements [SignerWithOpts], forwarding an *mldsa.Options +// Context to crypto/mldsa. +func (a *mldsaAlgorithm) SignWithOpts(key any, payload []byte, opts crypto.SignerOpts, _ io.Reader) ([]byte, error) { + sk, err := a.privateKey(key) + if err != nil { + return nil, fmt.Errorf(`dsig.SignWithOpts: %w`, err) + } + // Validated but deliberately not narrowed. SignMLDSA takes a + // crypto.SignerOpts, so converting to a typed nil here would hand + // crypto/mldsa a non-nil interface holding a nil pointer. + if _, err := mldsaOptions(opts); err != nil { + return nil, fmt.Errorf(`dsig.SignWithOpts: %w`, err) + } + return SignMLDSA(sk, payload, opts) +} + +func (a *mldsaAlgorithm) Verify(key any, payload, signature []byte) error { + pk, err := a.publicKey(key) + if err != nil { + return fmt.Errorf(`dsig.Verify: %w`, err) + } + return VerifyMLDSA(pk, payload, signature, nil) +} + +// VerifyWithOpts implements [VerifierWithOpts]. See [SignerWithOpts] for the +// rationale on rejecting a foreign opts type. +func (a *mldsaAlgorithm) VerifyWithOpts(key any, payload, signature []byte, opts crypto.SignerOpts) error { + pk, err := a.publicKey(key) + if err != nil { + return fmt.Errorf(`dsig.VerifyWithOpts: %w`, err) + } + mldsaOpts, err := mldsaOptions(opts) + if err != nil { + return fmt.Errorf(`dsig.VerifyWithOpts: %w`, err) + } + return VerifyMLDSA(pk, payload, signature, mldsaOpts) +} diff --git a/vendor/github.com/lestrrat-go/dsig/sign.go b/vendor/github.com/lestrrat-go/dsig/sign.go index eb57f5eec6..7d8b9340b3 100644 --- a/vendor/github.com/lestrrat-go/dsig/sign.go +++ b/vendor/github.com/lestrrat-go/dsig/sign.go @@ -14,10 +14,34 @@ import ( // rr is an io.Reader that provides randomness for signing. If rr is nil, it defaults to rand.Reader. // Not all algorithms require this parameter, but it is included for consistency. // 99% of the time, you can pass nil for rr, and it will work fine. +// +// Deprecated in spirit: in the next major release of dsig (v2), the +// signature of Sign will change to match [SignWithOpts], i.e. it will +// accept an additional [crypto.SignerOpts] parameter immediately before +// rr. Callers that need to pass per-call options today should use +// [SignWithOpts]; callers that do not can keep using Sign and migrate +// when v2 ships by threading a nil opts argument through at the call +// site. func Sign(key any, alg string, payload []byte, rr io.Reader) ([]byte, error) { + return SignWithOpts(key, alg, payload, nil, rr) +} + +// SignWithOpts is like [Sign] but threads an optional [crypto.SignerOpts] +// through to the underlying signer. For built-in families (HMAC, RSA, +// ECDSA, EdDSA) the opts argument is ignored — those algorithms have no +// per-call options the dsig layer understands. For Custom-family +// algorithms whose Meta implements [SignerWithOpts], the opts are +// forwarded; otherwise the plain [Signer.Sign] method is called and +// opts are dropped. +// +// This function exists as a transitional API. In the next major release +// of dsig (v2) it will be removed and its signature will become the +// canonical shape of [Sign]. Code that uses SignWithOpts today will need +// a mechanical rename to Sign (and nothing else) when v2 ships. +func SignWithOpts(key any, alg string, payload []byte, opts crypto.SignerOpts, rr io.Reader) ([]byte, error) { info, ok := GetAlgorithmInfo(alg) if !ok { - return nil, fmt.Errorf(`dsig.Sign: unsupported signature algorithm %q`, alg) + return nil, fmt.Errorf(`dsig.SignWithOpts: unsupported signature algorithm %q`, alg) } switch info.Family { @@ -29,10 +53,10 @@ func Sign(key any, alg string, payload []byte, rr io.Reader) ([]byte, error) { return dispatchECDSASign(key, info, payload, rr) case EdDSAFamily: return dispatchEdDSASign(key, info, payload, rr) - case Custom: - return dispatchCustomSign(key, info, payload, rr) + case Custom, MLDSAFamily: + return dispatchMetaSign(key, info, payload, opts, rr) default: - return nil, fmt.Errorf(`dsig.Sign: unsupported signature family %q`, info.Family) + return nil, fmt.Errorf(`dsig.SignWithOpts: unsupported signature family %q`, info.Family) } } @@ -100,7 +124,10 @@ func dispatchECDSASign(key any, info AlgorithmInfo, payload []byte, rr io.Reader return SignECDSA(privkey, payload, meta.Hash, rr) } -func dispatchCustomSign(key any, info AlgorithmInfo, payload []byte, rr io.Reader) ([]byte, error) { +func dispatchMetaSign(key any, info AlgorithmInfo, payload []byte, opts crypto.SignerOpts, rr io.Reader) ([]byte, error) { + if signer, ok := info.Meta.(SignerWithOpts); ok { + return signer.SignWithOpts(key, payload, opts, rr) + } signer, ok := info.Meta.(Signer) if !ok { return nil, fmt.Errorf(`dsig.Sign: algorithm has no signer registered`) @@ -121,6 +148,14 @@ func dispatchCustomSign(key any, info AlgorithmInfo, payload []byte, rr io.Reade // // rr is an io.Reader that provides randomness for signing. If rr is nil, // it defaults to rand.Reader. +// +// Deprecated in spirit: in the next major release of dsig (v2), the +// signature of SignDigest will gain a [crypto.SignerOpts] parameter to +// align with [Sign]. No SignDigestWithOpts shim exists in v1 because +// Custom-family algorithms (the only ones that would benefit from +// per-call opts) are rejected outright today; once a DigestSigner +// interface for the Custom family is added, the opts parameter will +// appear at the same time. func SignDigest(key any, alg string, digest []byte, rr io.Reader) ([]byte, error) { info, ok := GetAlgorithmInfo(alg) if !ok { @@ -140,6 +175,11 @@ func SignDigest(key any, alg string, digest []byte, rr io.Reader) ([]byte, error return nil, fmt.Errorf(`dsig.SignDigest: EdDSA does not support digest-based signing`) case Custom: return nil, fmt.Errorf(`dsig.SignDigest: custom algorithms do not support digest-based signing`) + case MLDSAFamily: + // ML-DSA's pre-hashed mode takes a mu representative. That is a + // different thing from a plain digest; pass mu to Sign with + // crypto.MLDSAMu. + return nil, fmt.Errorf(`dsig.SignDigest: ML-DSA does not support digest-based signing`) default: return nil, fmt.Errorf(`dsig.SignDigest: unsupported signature family %q`, info.Family) } @@ -207,4 +247,3 @@ func dispatchECDSASignDigest(key any, info AlgorithmInfo, digest []byte, rr io.R } return PackECDSASignature(r, s, privkey.Curve.Params().BitSize) } - diff --git a/vendor/github.com/lestrrat-go/dsig/verify.go b/vendor/github.com/lestrrat-go/dsig/verify.go index 05ffe8e94f..5999e505c7 100644 --- a/vendor/github.com/lestrrat-go/dsig/verify.go +++ b/vendor/github.com/lestrrat-go/dsig/verify.go @@ -9,10 +9,33 @@ import ( ) // Verify verifies a digital signature using the specified key and algorithm. +// +// Deprecated in spirit: in the next major release of dsig (v2), the +// signature of Verify will change to match [VerifyWithOpts], i.e. it +// will accept an additional [crypto.SignerOpts] parameter at the end. +// Callers that need to pass per-call options today should use +// [VerifyWithOpts]; callers that do not can keep using Verify and +// migrate when v2 ships by threading a nil opts argument through at +// the call site. func Verify(key any, alg string, payload, signature []byte) error { + return VerifyWithOpts(key, alg, payload, signature, nil) +} + +// VerifyWithOpts is like [Verify] but threads an optional +// [crypto.SignerOpts] through to the underlying verifier. For built-in +// families (HMAC, RSA, ECDSA, EdDSA) the opts argument is ignored. For +// Custom-family algorithms whose Meta implements [VerifierWithOpts], +// the opts are forwarded; otherwise the plain [Verifier.Verify] method +// is called and opts are dropped. +// +// This function exists as a transitional API. In the next major release +// of dsig (v2) it will be removed and its signature will become the +// canonical shape of [Verify]. Code that uses VerifyWithOpts today will +// need a mechanical rename to Verify (and nothing else) when v2 ships. +func VerifyWithOpts(key any, alg string, payload, signature []byte, opts crypto.SignerOpts) error { info, ok := GetAlgorithmInfo(alg) if !ok { - return fmt.Errorf(`dsig.Verify: unsupported signature algorithm %q`, alg) + return fmt.Errorf(`dsig.VerifyWithOpts: unsupported signature algorithm %q`, alg) } switch info.Family { @@ -24,10 +47,10 @@ func Verify(key any, alg string, payload, signature []byte) error { return dispatchECDSAVerify(key, info, payload, signature) case EdDSAFamily: return dispatchEdDSAVerify(key, info, payload, signature) - case Custom: - return dispatchCustomVerify(key, info, payload, signature) + case Custom, MLDSAFamily: + return dispatchMetaVerify(key, info, payload, signature, opts) default: - return fmt.Errorf(`dsig.Verify: unsupported signature family %q`, info.Family) + return fmt.Errorf(`dsig.VerifyWithOpts: unsupported signature family %q`, info.Family) } } @@ -41,6 +64,14 @@ func Verify(key any, alg string, payload, signature []byte) error { // parameter is not used because it is already incorporated into the MAC. // // EdDSA and Custom families are not supported and return an error. +// +// Deprecated in spirit: in the next major release of dsig (v2), the +// signature of VerifyDigest will gain a [crypto.SignerOpts] parameter +// to align with [Verify]. No VerifyDigestWithOpts shim exists in v1 +// because Custom-family algorithms (the only ones that would benefit +// from per-call opts) are rejected outright today; once a +// DigestVerifier interface for the Custom family is added, the opts +// parameter will appear at the same time. func VerifyDigest(key any, alg string, digest, signature []byte) error { info, ok := GetAlgorithmInfo(alg) if !ok { @@ -62,6 +93,9 @@ func VerifyDigest(key any, alg string, digest, signature []byte) error { // TODO: a DigestVerifier interface (optional, checked here) would let // custom algorithms opt in to digest-based verification. return fmt.Errorf(`dsig.VerifyDigest: custom algorithms do not support digest-based verification`) + case MLDSAFamily: + // mu is derived from the message, so there is no digest to supply here. + return fmt.Errorf(`dsig.VerifyDigest: ML-DSA does not support digest-based verification`) default: return fmt.Errorf(`dsig.VerifyDigest: unsupported signature family %q`, info.Family) } @@ -196,7 +230,10 @@ func dispatchEdDSAVerify(key any, _ AlgorithmInfo, payload, signature []byte) er return VerifyEdDSA(pubkey, payload, signature) } -func dispatchCustomVerify(key any, info AlgorithmInfo, payload, signature []byte) error { +func dispatchMetaVerify(key any, info AlgorithmInfo, payload, signature []byte, opts crypto.SignerOpts) error { + if verifier, ok := info.Meta.(VerifierWithOpts); ok { + return verifier.VerifyWithOpts(key, payload, signature, opts) + } verifier, ok := info.Meta.(Verifier) if !ok { return fmt.Errorf(`dsig.Verify: algorithm has no verifier registered`) diff --git a/vendor/github.com/lestrrat-go/httprc/v3/Changes b/vendor/github.com/lestrrat-go/httprc/v3/Changes index 001c8c5444..32357a060c 100644 --- a/vendor/github.com/lestrrat-go/httprc/v3/Changes +++ b/vendor/github.com/lestrrat-go/httprc/v3/Changes @@ -1,6 +1,13 @@ Changes ======= +v3.0.6 07 Jun 2026 + * Back off on HTTP fetch failure (connection refused, DNS failure, + timeout) by scheduling the next refresh at now+MinInterval, instead + of re-dispatching the resource in a tight ~1s loop (#119, #130) + * Document anchoring of RegexpWhitelist patterns + (e.g. `^https://example\.com/`) and add a runnable example (#125) + v3.0.5 30 Mar 2026 * Fix periodic check deadlock when number of ready resources exceeds outgoing channel buffer, which caused circular wait between controller diff --git a/vendor/github.com/lestrrat-go/httprc/v3/README.md b/vendor/github.com/lestrrat-go/httprc/v3/README.md index 68239669a2..4f353d29d2 100644 --- a/vendor/github.com/lestrrat-go/httprc/v3/README.md +++ b/vendor/github.com/lestrrat-go/httprc/v3/README.md @@ -65,6 +65,58 @@ If the values obtained from the headers fall within that range, the value from t used. If the value is larger than the maximum, the maximum is used. If the value is lower than the minimum, the minimum is used. +# Whitelisting URLs + +By default the client allows all URLs. If you store resources whose URLs come from +untrusted sources, you should restrict what can be fetched by passing a whitelist +via `httprc.WithWhitelist`. Several implementations are provided: `BlockAllWhitelist`, +`InsecureWhitelist` (allow all), `MapWhitelist` (exact string match), and +`RegexpWhitelist`. + +## A note on `RegexpWhitelist` patterns + +`RegexpWhitelist` matches each URL with `(*regexp.Regexp).MatchString`, which returns +true when the pattern matches **any substring** of the URL. Patterns are **not** +anchored for you, so a naive pattern can allow far more than you intend. + +Consider the difference between these two patterns: + +```go +// BAD: unanchored, dots unescaped +regexp.MustCompile(`http://example.com`) + +// GOOD: anchored at the start, dots escaped, host terminated with `/` +regexp.MustCompile(`^https://example\.com/`) +``` + +The unanchored `http://example.com` pattern will happily allow URLs such as: + +- `http://example.com.attacker.com/evil` — the real host is `attacker.com`; the + pattern only required `example.com` to appear *somewhere*, and without a trailing + `/` it does not stop at the end of the host. +- `http://attacker.com/?redirect=http://example.com` — the pattern appears inside + the query string, so the match succeeds even though the host is `attacker.com`. +- `httpsX//exampleYcom` — `.` is the regular-expression "any character" + metacharacter, so the dots match more than literal dots. + +To pin a pattern to a specific origin: + +1. **Anchor the start** with `^` so the match must begin at the start of the URL. +2. **Escape the dots** (`\.`) so they only match a literal `.`. +3. **Terminate the host** with `/` so `example.com` cannot be extended into + `example.com.attacker.com`. + +A couple of edge cases to keep in mind: + +- Requiring the trailing `/` means the bare origin `https://example.com` (no path) + will not match. Add a second pattern such as `^https://example\.com$` if you need + to allow it. +- If your URLs may include a port, allow for it explicitly, e.g. + `^https://example\.com(:\d+)?/`. + +See `ExampleRegexpWhitelist` in `whitelist_example_test.go` for a runnable +demonstration of the difference between anchored and unanchored patterns. + # SYNOPSIS diff --git a/vendor/github.com/lestrrat-go/httprc/v3/resource.go b/vendor/github.com/lestrrat-go/httprc/v3/resource.go index 0f0d140d27..1e957cbfa3 100644 --- a/vendor/github.com/lestrrat-go/httprc/v3/resource.go +++ b/vendor/github.com/lestrrat-go/httprc/v3/resource.go @@ -232,6 +232,10 @@ func (r *ResourceBase[T]) Sync(ctx context.Context) error { traceSink.Put(ctx, fmt.Sprintf("httprc.Resource.Sync: fetching %q", r.u)) res, err := httpcl.Do(req) if err != nil { + // Schedule retry after MinInterval so that connection failures + // don't cause a tight retry loop (the resource's Next stays at + // epoch if we don't update it here). + r.SetNext(time.Now().Add(r.MinInterval())) return fmt.Errorf(`httprc.Resource.Sync: failed to execute HTTP request: %w`, err) } defer res.Body.Close() diff --git a/vendor/github.com/lestrrat-go/httprc/v3/whitelist.go b/vendor/github.com/lestrrat-go/httprc/v3/whitelist.go index 74ef2a1be6..9a55d6e5c5 100644 --- a/vendor/github.com/lestrrat-go/httprc/v3/whitelist.go +++ b/vendor/github.com/lestrrat-go/httprc/v3/whitelist.go @@ -49,6 +49,29 @@ func (InsecureWhitelist) IsAllowed(_ string) bool { return true } // RegexpWhitelist is a jwk.Whitelist object comprised of a list of *regexp.Regexp // objects. All entries in the list are tried until one matches. If none of the // *regexp.Regexp objects match, then the URL is deemed unallowed. +// +// Matching is performed using (*regexp.Regexp).MatchString, which succeeds when +// the pattern matches ANY substring of the URL — it is NOT anchored automatically. +// This has important security implications: a pattern like `http://example.com` +// will match URLs you almost certainly did not intend to allow, such as +// `http://example.com.attacker.com/` (the host is actually attacker.com) or +// `http://attacker.com/?u=http://example.com` (the pattern appears in the query). +// +// To restrict to a specific origin, anchor the pattern at the start with `^`, +// escape the dots in the host (`.` is the "any character" metacharacter in a +// regular expression), and terminate the host with a `/` so that it cannot be +// extended into a subdomain: +// +// // GOOD: only matches the example.com origin and its paths +// regexp.MustCompile(`^https://example\.com/`) +// +// // BAD: also matches example.com.attacker.com, attacker.com/?x=http://example.com, httpsX//exampleYcom, ... +// regexp.MustCompile(`http://example.com`) +// +// Note that requiring a trailing `/` means the bare origin URL `https://example.com` +// (no path) will not match; register an additional pattern such as +// `^https://example\.com$` if you need to allow it. Likewise, account for an +// optional port (e.g. `^https://example\.com(:\d+)?/`) if your URLs may include one. type RegexpWhitelist struct { mu sync.RWMutex patterns []*regexp.Regexp diff --git a/vendor/github.com/lestrrat-go/jwx/v3/BUILD b/vendor/github.com/lestrrat-go/jwx/v3/BUILD index 2759408882..5da405b23f 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/BUILD +++ b/vendor/github.com/lestrrat-go/jwx/v3/BUILD @@ -1,9 +1,15 @@ -load("@rules_go//go:def.bzl", "go_library", "go_test") load("@gazelle//:def.bzl", "gazelle") +load("@rules_go//go:def.bzl", "go_library", "go_test") # gazelle:prefix github.com/lestrrat-go/jwx/v3 # gazelle:go_naming_convention import_alias +# Scratch directories that are not part of the module. Without these, +# gazelle walks bazel's own output tree under .gauntlet and rewrites every +# BUILD file to point at copies of the repo it finds in there. +# gazelle:exclude .gauntlet +# gazelle:exclude .tmp + gazelle(name = "gazelle") go_library( diff --git a/vendor/github.com/lestrrat-go/jwx/v3/Changes b/vendor/github.com/lestrrat-go/jwx/v3/Changes index c5eeebf6dd..34318280c5 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/Changes +++ b/vendor/github.com/lestrrat-go/jwx/v3/Changes @@ -4,6 +4,62 @@ Changes v3 has many incompatibilities with v2. To see the full list of differences between v2 and v3, please read the Changes-v3.md file (https://github.com/lestrrat-go/jwx/blob/develop/v3/Changes-v3.md) +v3.3.0 8 Sep 2026 + * [jwt][jws][jwe][jwk] Custom claim, header, and JWK field names are now + JSON-escaped on output. Previously a name was written between the quotes + as is, so a name containing `"` could close its own member and add + members the application never set. For example, calling `Set` with the + name `x":0,"admin` produced a signed token containing `"admin":true`. + Every name now yields exactly one member, and names that need no + escaping serialize exactly as before. + + If your application accepts custom names from callers, an exact-match + allowlist was never affected. A blocklist of reserved names, or an + allowlist by namespace prefix, could be bypassed by this defect. Both are + reasonable designs; the bug was in the serializer. Prefer an exact-match + allowlist, and if you accept a prefix, require the rest of the name to be + a plain identifier. + + Fixed in v4.5.0 and v3.3.0. v2, v1, and v0 contain the same code and are + unmaintained; see SECURITY.md. (GHSA-4cf7-xm37-g63h) + + * [jws] Added `jws.WithStrictECDSA(bool)`, a `jws.Sign` option that rejects + anything RFC 7518 forbids for an ECDSA signature. Today that is Section + 3.4's binding of ES256 to P-256, ES384 to P-384, and ES512 to P-521, so + signing a P-521 key under `jwa.ES256()` fails instead of producing a JWS + that other JOSE implementations reject. + + The default is unchanged: without the option, a mismatched curve and + algorithm still sign exactly as before. `jws.Verify` is unaffected either + way. `jwt.Sign` callers can reach the option through + `jwt.WithSignOption(jws.WithStrictECDSA(true))`. (#2323) + +v3.2.0 27 Jul 2026 + * [jwe] Correct the JSON `"aad"` member so it contains only + BASE64URL of the external Additional Authenticated Data, rather than the + combined value used as the content-encryption AAD. Add + `jwe.WithAuthenticateData` for encrypting JSON JWEs with external AAD; + the value is included in the shared AEAD input for all recipients, and + compact serialization rejects non-empty external AAD. (#2276, #2278) + + * [jwk] Added opt-in retention of unparseable JWK Set entries. Passing + `jwk.WithStrictKeySetParsing(false)` to `jwk.Parse` (or setting it + globally via `jwk.Configure`) keeps an entry whose key type is not + understood — for example a post-quantum key published alongside + classical keys — as a `jwk.UnsupportedKey` placeholder instead of + failing the whole set (RFC 7517 §5). The placeholder preserves the + entry's original JSON (marshaling round-trips losslessly) and the + parse error via `Reason()`; use `jwk.IsUnsupportedKey` to detect one. + The default is unchanged: v3 still fails the whole set on the first + unparseable entry, so existing callers see no difference. The same + option exists in v4 with the opposite default (v4 retains by + default, v3 stays strict by default); call sites that pass the + option explicitly keep the same meaning across the v3→v4 migration. + Placeholders + are rejected by `jws`/`jwe` key selection, `jwk.Export`, + `jwk.AssignKeyID`, and `jwk.PublicSetOf` (which accepts a new + `jwk.WithOmitUnsupportedKeys(true)` to drop them). (#2263) + v3.1.1 7 May 2026 * [jws] Coordinated RFC 7797 `b64=false` handling pass: `jws.Verify` rejects payloads with `b64=false` unless `b64` is also listed in diff --git a/vendor/github.com/lestrrat-go/jwx/v3/SECURITY.md b/vendor/github.com/lestrrat-go/jwx/v3/SECURITY.md index 601dced5cd..a8d9f83d29 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/SECURITY.md +++ b/vendor/github.com/lestrrat-go/jwx/v3/SECURITY.md @@ -2,13 +2,21 @@ ## Supported Versions -Most recent two major versions will receive security updates +Security fixes are published for the versions marked below. The +[State of support](https://github.com/lestrrat-go/jwx/discussions/1079) +discussion is the canonical, up-to-date statement; this table summarizes it. | Version | Supported | | -------- | ------------------ | -| v3.x.x | :white_check_mark: | -| v2.x.x | :white_check_mark: | -| < v2.0.0 | :x: | +| v4.x.x | :white_check_mark: Current release | +| v3.x.x | :white_check_mark: Previous release; receives regular fixes | +| v2.x.x | :x: Unmaintained. Do not use | +| v1.x.x | :x: Unmaintained. Do not use | +| < v1.0.0 | :x: Unmaintained. Do not use | + +Unmaintained versions receive no fixes of any kind, including for issues +already fixed in a supported version. Each advisory names the versions that +carry the fix; a version not named there stays affected. ## Reporting a Vulnerability diff --git a/vendor/github.com/lestrrat-go/jwx/v3/internal/json/BUILD.bazel b/vendor/github.com/lestrrat-go/jwx/v3/internal/json/BUILD.bazel index 4e2dbe12b7..29b9544554 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/internal/json/BUILD.bazel +++ b/vendor/github.com/lestrrat-go/jwx/v3/internal/json/BUILD.bazel @@ -1,4 +1,4 @@ -load("@rules_go//go:def.bzl", "go_library") +load("@rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "json", @@ -9,7 +9,10 @@ go_library( ], importpath = "github.com/lestrrat-go/jwx/v3/internal/json", visibility = ["//:__subpackages__"], - deps = ["//internal/base64"], + deps = [ + "//internal/base64", + "//internal/tokens", + ], ) alias( @@ -17,3 +20,12 @@ alias( actual = ":json", visibility = ["//:__subpackages__"], ) + +go_test( + name = "json_test", + srcs = ["json_test.go"], + deps = [ + ":json", + "@com_github_stretchr_testify//require", + ], +) diff --git a/vendor/github.com/lestrrat-go/jwx/v3/internal/json/json.go b/vendor/github.com/lestrrat-go/jwx/v3/internal/json/json.go index 4dec2b806c..91c5cbd1bc 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/internal/json/json.go +++ b/vendor/github.com/lestrrat-go/jwx/v3/internal/json/json.go @@ -7,6 +7,7 @@ import ( "sync/atomic" "github.com/lestrrat-go/jwx/v3/internal/base64" + "github.com/lestrrat-go/jwx/v3/internal/tokens" ) var useNumber atomic.Uint32 @@ -164,3 +165,31 @@ func (dc *decodeCtx) Registry() *Registry { func (dc *decodeCtx) StrictStrings() bool { return dc.strictStrings } + +// WriteQuotedKey writes key as a quoted JSON object member name followed by +// the separating colon and a space. +// +// Member names come from public methods such as Set and Builder.Claim, so +// they may contain any byte, including `"`. A name copied raw between the +// quotes could end its own member and start further ones, so the serialized +// object would no longer match the one the caller built +// (GHSA-4cf7-xm37-g63h). A name that needs no escaping is written directly, +// which keeps the common path free of allocations. Every other name goes +// through the JSON string encoder. +func WriteQuotedKey(buf *bytes.Buffer, key string) error { + if tokens.IsJSONSafeASCII(key) { + buf.WriteByte(tokens.DoubleQuote) + buf.WriteString(key) + buf.WriteString(`": `) + return nil + } + + encoded, err := Marshal(key) + if err != nil { + return fmt.Errorf(`failed to encode object member name: %w`, err) + } + buf.Write(encoded) + buf.WriteByte(tokens.Colon) + buf.WriteByte(' ') + return nil +} diff --git a/vendor/github.com/lestrrat-go/jwx/v3/internal/keyconv/BUILD.bazel b/vendor/github.com/lestrrat-go/jwx/v3/internal/keyconv/BUILD.bazel index d46d2f3814..f6bac26132 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/internal/keyconv/BUILD.bazel +++ b/vendor/github.com/lestrrat-go/jwx/v3/internal/keyconv/BUILD.bazel @@ -8,7 +8,6 @@ go_library( deps = [ "//jwk", "@com_github_lestrrat_go_blackmagic//:blackmagic", - "@org_golang_x_crypto//ed25519", ], ) diff --git a/vendor/github.com/lestrrat-go/jwx/v3/internal/pool/BUILD.bazel b/vendor/github.com/lestrrat-go/jwx/v3/internal/pool/BUILD.bazel index c48330e278..04dbd6697a 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/internal/pool/BUILD.bazel +++ b/vendor/github.com/lestrrat-go/jwx/v3/internal/pool/BUILD.bazel @@ -21,11 +21,9 @@ alias( go_test( name = "pool_test", - srcs = [ - "byte_slice_test.go", - ], + srcs = ["byte_slice_test.go"], deps = [ ":pool", "@com_github_stretchr_testify//require", ], -) \ No newline at end of file +) diff --git a/vendor/github.com/lestrrat-go/jwx/v3/internal/tokens/tokens.go b/vendor/github.com/lestrrat-go/jwx/v3/internal/tokens/tokens.go index 2af3b88de1..864a86c42f 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/internal/tokens/tokens.go +++ b/vendor/github.com/lestrrat-go/jwx/v3/internal/tokens/tokens.go @@ -11,6 +11,20 @@ const ( Period = '.' ) +// IsJSONSafeASCII reports whether s can be concatenated into a +// hand-built JSON string literal without escaping. Any byte that +// would require a JSON escape (control bytes, `"`, `\`) or any +// non-ASCII byte disqualifies the value. +func IsJSONSafeASCII(s string) bool { + for i := range len(s) { + c := s[i] + if c < 0x20 || c >= 0x7f || c == '"' || c == '\\' { + return false + } + } + return true +} + // Cryptographic key sizes const ( KeySize16 = 16 diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwa/BUILD.bazel b/vendor/github.com/lestrrat-go/jwx/v3/jwa/BUILD.bazel index 6d0af7efb3..cc8bd5bd26 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jwa/BUILD.bazel +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwa/BUILD.bazel @@ -24,20 +24,20 @@ go_library( go_test( name = "jwa_test", srcs = [ + "builtin_registry_test.go", "compression_gen_test.go", "content_encryption_gen_test.go", + "cross_kind_test.go", "elliptic_gen_test.go", "jwa_test.go", "key_encryption_gen_test.go", "key_type_gen_test.go", "options_gen_test.go", + "registry_snapshot_test.go", "signature_gen_test.go", ], embed = [":jwa"], - deps = [ - "@com_github_stretchr_testify//require", - "@com_github_lestrrat_go_option_v2//:option", - ], + deps = ["@com_github_stretchr_testify//require"], ) alias( diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwe/BUILD.bazel b/vendor/github.com/lestrrat-go/jwx/v3/jwe/BUILD.bazel index 0719efd2dc..82ef013db7 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jwe/BUILD.bazel +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwe/BUILD.bazel @@ -23,40 +23,47 @@ go_library( deps = [ "//cert", "//internal/base64", - "//transform", "//internal/json", - "//internal/tokens", "//internal/keyconv", "//internal/pool", + "//internal/tokens", "//jwa", "//jwe/internal/aescbc", - "//jwe/internal/cipher", "//jwe/internal/content_crypt", "//jwe/internal/keygen", "//jwe/jwebb", "//jwk", + "//transform", "@com_github_lestrrat_go_blackmagic//:blackmagic", "@com_github_lestrrat_go_option_v2//:option", - "@org_golang_x_crypto//pbkdf2", ], ) go_test( name = "jwe_test", srcs = [ + "bench_encrypt_test.go", + "encrypt_aad_test.go", "filter_test.go", + "fuzz_test.go", "gh402_test.go", "headers_test.go", + "jwe_aad_internal_test.go", + "jwe_crit_test.go", "jwe_test.go", + "message_aad_test.go", "message_test.go", "options_gen_test.go", + "recipient_headers_test.go", "speed_test.go", + "unsupported_key_test.go", ], embed = [":jwe"], deps = [ "//cert", "//internal/json", "//internal/jwxtest", + "//internal/tokens", "//jwa", "//jwk", "@com_github_stretchr_testify//require", diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwe/headers_gen.go b/vendor/github.com/lestrrat-go/jwx/v3/jwe/headers_gen.go index 5390c2be45..53ee8ca5dd 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jwe/headers_gen.go +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwe/headers_gen.go @@ -962,9 +962,9 @@ func (h *stdHeaders) MarshalJSON() ([]byte, error) { if i > 0 { buf.WriteByte(tokens.Comma) } - buf.WriteByte('"') - buf.WriteString(pair.Name) - buf.WriteString(`": `) + if err := json.WriteQuotedKey(buf, pair.Name); err != nil { + return nil, fmt.Errorf(`failed to encode field name %q: %w`, pair.Name, err) + } buf.Write(pair.Value.([]byte)) } buf.WriteByte(tokens.CloseCurlyBracket) diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwe/internal/aescbc/BUILD.bazel b/vendor/github.com/lestrrat-go/jwx/v3/jwe/internal/aescbc/BUILD.bazel index 4ed4c53fa3..20d86e5252 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jwe/internal/aescbc/BUILD.bazel +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwe/internal/aescbc/BUILD.bazel @@ -12,7 +12,7 @@ go_test( name = "aescbc_test", srcs = ["aescbc_test.go"], embed = [":aescbc"], - deps = ["@com_github_stretchr_testify//require"] + deps = ["@com_github_stretchr_testify//require"], ) alias( diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwe/internal/cipher/BUILD.bazel b/vendor/github.com/lestrrat-go/jwx/v3/jwe/internal/cipher/BUILD.bazel index cf642c744d..3a67551111 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jwe/internal/cipher/BUILD.bazel +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwe/internal/cipher/BUILD.bazel @@ -9,10 +9,9 @@ go_library( importpath = "github.com/lestrrat-go/jwx/v3/jwe/internal/cipher", visibility = ["//:__subpackages__"], deps = [ - "//jwa", + "//internal/tokens", "//jwe/internal/aescbc", "//jwe/internal/keygen", - "//internal/tokens", ], ) @@ -21,7 +20,6 @@ go_test( srcs = ["cipher_test.go"], deps = [ ":cipher", - "//jwa", "//internal/tokens", "@com_github_stretchr_testify//require", ], diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwe/internal/concatkdf/BUILD.bazel b/vendor/github.com/lestrrat-go/jwx/v3/jwe/internal/concatkdf/BUILD.bazel index 59aeb2cd27..3665c71f57 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jwe/internal/concatkdf/BUILD.bazel +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwe/internal/concatkdf/BUILD.bazel @@ -9,7 +9,10 @@ go_library( go_test( name = "concatkdf_test", - srcs = ["concatkdf_test.go"], + srcs = [ + "bench_test.go", + "concatkdf_test.go", + ], embed = [":concatkdf"], deps = [ "//jwa", diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwe/internal/keygen/BUILD.bazel b/vendor/github.com/lestrrat-go/jwx/v3/jwe/internal/keygen/BUILD.bazel index bde8eb68f7..91f5b3973b 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jwe/internal/keygen/BUILD.bazel +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwe/internal/keygen/BUILD.bazel @@ -1,4 +1,4 @@ -load("@rules_go//go:def.bzl", "go_library") +load("@rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "keygen", @@ -9,10 +9,8 @@ go_library( importpath = "github.com/lestrrat-go/jwx/v3/jwe/internal/keygen", visibility = ["//:__subpackages__"], deps = [ - "//internal/ecutil", - "//jwa", - "//jwe/internal/concatkdf", "//internal/tokens", + "//jwe/internal/concatkdf", "//jwk", ], ) @@ -22,3 +20,12 @@ alias( actual = ":keygen", visibility = ["//jwe:__subpackages__"], ) + +go_test( + name = "keygen_test", + srcs = ["keygen_test.go"], + deps = [ + ":keygen", + "@com_github_stretchr_testify//require", + ], +) diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwe/jwe.go b/vendor/github.com/lestrrat-go/jwx/v3/jwe/jwe.go index 706efaaa27..6bf043ecb6 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jwe/jwe.go +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwe/jwe.go @@ -877,6 +877,7 @@ type encryptContext struct { compression jwa.CompressionAlgorithm format int pbes2Count int + authenticatedData []byte builders []*recipientBuilder protected Headers legacyHeaderMerging bool @@ -897,6 +898,7 @@ func freeEncryptContext(ec *encryptContext) *encryptContext { ec.compression = jwa.NoCompress() ec.format = fmtCompact ec.pbes2Count = 0 + ec.authenticatedData = nil ec.builders = ec.builders[:0] ec.protected = nil return ec @@ -949,6 +951,12 @@ func (ec *encryptContext) ProcessOptions(options []EncryptOption) error { return err } ec.compression = comp + case identAuthenticateData{}: + var aad []byte + if err := option.Value(&aad); err != nil { + return err + } + ec.authenticatedData = aad case identMergeProtectedHeaders{}: var mp bool if err := option.Value(&mp); err != nil { @@ -994,6 +1002,10 @@ func (ec *encryptContext) ProcessOptions(options []EncryptOption) error { } } + if len(ec.authenticatedData) > 0 && ec.format == fmtCompact { + return fmt.Errorf(`cannot use compact serialization with external authenticated data (use WithJSON())`) + } + if useRawCEK { if len(ec.builders) != 1 { return fmt.Errorf(`multiple recipients for ECDH-ES/DIRECT mode are not supported`) @@ -1193,12 +1205,13 @@ func (ec *encryptContext) EncryptMessage(payload []byte, cek []byte) ([]byte, er } } - aad, err := protected.Encode() + protectedAAD, err := protected.Encode() if err != nil { return nil, fmt.Errorf(`failed to base64 encode protected headers: %w`, err) } - iv, ciphertext, tag, err := contentcrypt.Encrypt(cek, payload, aad) + contentAAD := concatAAD(protectedAAD, base64.Encode(ec.authenticatedData)) + iv, ciphertext, tag, err := contentcrypt.Encrypt(cek, payload, contentAAD) if err != nil { return nil, fmt.Errorf(`failed to encrypt payload: %w`, err) } @@ -1207,7 +1220,7 @@ func (ec *encryptContext) EncryptMessage(payload []byte, cek []byte) ([]byte, er // pre-encoded headers and raw fields, avoiding the full Message // construction and redundant header re-encoding that Compact() does. if ec.format == fmtCompact { - return compactSerialize(aad, recipients[0].EncryptedKey(), iv, ciphertext, tag), nil + return compactSerialize(protectedAAD, recipients[0].EncryptedKey(), iv, ciphertext, tag), nil } msg := msgPool.Get() @@ -1228,6 +1241,11 @@ func (ec *encryptContext) EncryptMessage(payload []byte, cek []byte) ([]byte, er if err := msg.Set(TagKey, tag); err != nil { return nil, fmt.Errorf(`failed to set %s: %w`, TagKey, err) } + if len(ec.authenticatedData) > 0 { + if err := msg.Set(AuthenticatedDataKey, ec.authenticatedData); err != nil { + return nil, fmt.Errorf(`failed to set %s: %w`, AuthenticatedDataKey, err) + } + } switch ec.format { case fmtJSON: diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwe/jwebb/BUILD.bazel b/vendor/github.com/lestrrat-go/jwx/v3/jwe/jwebb/BUILD.bazel index c410a05cdf..03df94b61c 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jwe/jwebb/BUILD.bazel +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwe/jwebb/BUILD.bazel @@ -4,6 +4,7 @@ go_library( name = "jwebb", srcs = [ "content_cipher.go", + "jwebb.go", "key_decrypt_asymmetric.go", "key_decrypt_symmetric.go", "key_encrypt_asymmetric.go", @@ -16,11 +17,11 @@ go_library( deps = [ "//internal/keyconv", "//internal/pool", + "//internal/tokens", "//jwe/internal/cipher", "//jwe/internal/concatkdf", "//jwe/internal/content_crypt", "//jwe/internal/keygen", - "//internal/tokens", "@org_golang_x_crypto//pbkdf2", ], ) @@ -32,12 +33,18 @@ go_test( "jwebb_test.go", "keywrap_test.go", ], - embed = [":jwebb"], deps = [ + ":jwebb", "//internal/jwxtest", + "//internal/tokens", "//jwa", "//jwe/internal/keygen", - "//internal/tokens", "@com_github_stretchr_testify//require", ], -) \ No newline at end of file +) + +alias( + name = "go_default_library", + actual = ":jwebb", + visibility = ["//visibility:public"], +) diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwe/key_provider.go b/vendor/github.com/lestrrat-go/jwx/v3/jwe/key_provider.go index 81bb5b6ec7..62fbf1c849 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jwe/key_provider.go +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwe/key_provider.go @@ -108,6 +108,11 @@ type keySetProvider struct { } func (kp *keySetProvider) selectKey(sink KeySink, key jwk.Key, r Recipient, msg *Message) error { + if uk, ok := key.(jwk.UnsupportedKey); ok { + kid, _ := uk.KeyID() + return fmt.Errorf(`key %q has unsupported key type %q and cannot be used for decryption; an extension module may be required to parse it: %w`, kid, uk.KeyType().String(), uk.Reason()) + } + if usage, ok := key.KeyUsage(); ok { if usage != "" && usage != jwk.ForEncryption.String() { kid, _ := key.KeyID() diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwe/message.go b/vendor/github.com/lestrrat-go/jwx/v3/jwe/message.go index 22c6e6660a..583815ddd3 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jwe/message.go +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwe/message.go @@ -227,32 +227,23 @@ func (m *Message) MarshalJSON() ([]byte, error) { }) } - var encodedProtectedHeaders []byte if h := m.ProtectedHeaders(); h != nil { v, err := h.Encode() if err != nil { return nil, fmt.Errorf(`failed to encode protected headers: %w`, err) } - encodedProtectedHeaders = v - if len(encodedProtectedHeaders) <= 2 { // '{}' - encodedProtectedHeaders = nil - } else { + if len(v) > 2 { // '{}' fields = append(fields, jsonKV{ Key: ProtectedHeadersKey, - Value: fmt.Sprintf("%q", encodedProtectedHeaders), + Value: fmt.Sprintf("%q", v), }) } } if aad := m.AuthenticatedData(); len(aad) > 0 { - aad = base64.Encode(aad) - if encodedProtectedHeaders != nil { - aad = concatAAD(encodedProtectedHeaders, aad) - } - buf.Reset() - if err := enc.Encode(aad); err != nil { + if err := enc.Encode(base64.EncodeToString(aad)); err != nil { return nil, fmt.Errorf(`failed to encode %s field: %w`, AuthenticatedDataKey, err) } fields = append(fields, jsonKV{ @@ -377,7 +368,7 @@ func (m *Message) UnmarshalJSON(buf []byte) error { if proxy.Headers != nil || len(proxy.EncryptedKey) > 0 { recipient := NewRecipient() - // `"heders"` could be empty. If that's the case, just skip the + // `"headers"` could be empty. If that's the case, just skip the // following unmarshaling step if proxy.Headers != nil { hdrs := NewHeaders() diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwe/options.go b/vendor/github.com/lestrrat-go/jwx/v3/jwe/options.go index ab356f5d81..969e5bc24e 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jwe/options.go +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwe/options.go @@ -1,6 +1,8 @@ package jwe import ( + "bytes" + "github.com/lestrrat-go/jwx/v3/jwa" "github.com/lestrrat-go/jwx/v3/jwk" "github.com/lestrrat-go/option/v2" @@ -76,6 +78,16 @@ func WithProtectedHeaders(h Headers) EncryptOption { return &encryptOption{option.New(identProtectedHeaders{}, cloned)} } +// WithAuthenticateData specifies the external Additional Authenticated Data +// to use when encrypting a JSON JWE. +// +// The data is copied before it is stored in the option. External Additional +// Authenticated Data is not supported by compact serialization; pass +// WithJSON() to select JSON serialization. +func WithAuthenticateData(aad []byte) EncryptOption { + return &encryptOption{option.New(identAuthenticateData{}, bytes.Clone(aad))} +} + type withKey struct { alg jwa.KeyAlgorithm key any diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwe/options.yaml b/vendor/github.com/lestrrat-go/jwx/v3/jwe/options.yaml index 428e73e2b0..6bbb682c53 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jwe/options.yaml +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwe/options.yaml @@ -60,6 +60,13 @@ options: skip_option: true - ident: ProtectedHeaders skip_option: true + - ident: AuthenticateData + skip_option: true + interface: EncryptOption + argument_type: '[]byte' + comment: | + WithAuthenticateData specifies the external Additional Authenticated Data + to use when encrypting a JSON JWE. - ident: PerRecipientHeaders skip_option: true - ident: KeyProvider diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwe/options_gen.go b/vendor/github.com/lestrrat-go/jwx/v3/jwe/options_gen.go index 54962c5d04..11f1275b8a 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jwe/options_gen.go +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwe/options_gen.go @@ -169,6 +169,7 @@ type withKeySetSuboption struct { func (*withKeySetSuboption) withKeySetSuboption() {} +type identAuthenticateData struct{} type identCBCBufferSize struct{} type identCEK struct{} type identCompress struct{} @@ -193,6 +194,10 @@ type identProtectedHeaders struct{} type identRequireKid struct{} type identSerialization struct{} +func (identAuthenticateData) String() string { + return "WithAuthenticateData" +} + func (identCBCBufferSize) String() string { return "WithCBCBufferSize" } diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwk/BUILD.bazel b/vendor/github.com/lestrrat-go/jwx/v3/jwk/BUILD.bazel index 1bcb93a319..647b9a0ed5 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jwk/BUILD.bazel +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwk/BUILD.bazel @@ -5,6 +5,7 @@ go_library( srcs = [ "cache.go", "convert.go", + "doc.go", "ecdsa.go", "ecdsa_gen.go", "errors.go", @@ -25,6 +26,7 @@ go_library( "set.go", "symmetric.go", "symmetric_gen.go", + "unsupported.go", "usage.go", "whitelist.go", "x509.go", @@ -35,14 +37,14 @@ go_library( "//cert", "//internal/base64", "//internal/ecutil", - "//transform", "//internal/json", "//internal/pool", - "//internal/tokens", + "//internal/tokens", "//jwa", "//jwk/ecdsa", "//jwk/internal/registry", "//jwk/jwkbb", + "//transform", "@com_github_lestrrat_go_blackmagic//:blackmagic", "@com_github_lestrrat_go_httprc_v3//:httprc", "@com_github_lestrrat_go_option_v2//:option", @@ -52,13 +54,20 @@ go_library( go_test( name = "jwk_test", srcs = [ + "bench_set_test.go", + "ecdsa_test.go", "filter_test.go", + "fuzz_test.go", "headers_test.go", - "jwk_internal_test.go", "jwk_test.go", + "jwk_zero_on_error_test.go", + "okp_length_test.go", "options_gen_test.go", "refresh_test.go", + "rsa_thumbprint_test.go", + "rsa_validate_test.go", "set_test.go", + "unsupported_test.go", "x5c_test.go", ], data = glob(["testdata/**"]), @@ -72,7 +81,7 @@ go_test( "//internal/tokens", "//jwa", "//jwk/ecdsa", - "//jws", + "//jwk/jwkunsafe", "@com_github_lestrrat_go_blackmagic//:blackmagic", "@com_github_lestrrat_go_httprc_v3//:httprc", "@com_github_lestrrat_go_httprc_v3//tracesink", diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwk/convert.go b/vendor/github.com/lestrrat-go/jwx/v3/jwk/convert.go index 4cf7bb6450..779e6b8ca5 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jwk/convert.go +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwk/convert.go @@ -409,6 +409,11 @@ func bytesToKey(src any) (Key, error) { // especially when the object implements the `jwk.Key` interface via // embedding. func Export(key Key, dst any) error { + if uk, ok := key.(UnsupportedKey); ok { + kid, _ := uk.KeyID() + return fmt.Errorf(`jwk.Export: cannot export an unsupported key (kty=%q, kid=%q) that could not be parsed; an extension module may be required: %w`, uk.KeyType().String(), kid, uk.Reason()) + } + // dst better be a pointer rv := reflect.ValueOf(dst) if rv.Kind() != reflect.Ptr { diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwk/doc.go b/vendor/github.com/lestrrat-go/jwx/v3/jwk/doc.go index b4f6e164e2..b7f9a07c68 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jwk/doc.go +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwk/doc.go @@ -29,7 +29,7 @@ // jws.Sign([]byte(`...`), jws.WithKey(jwa.RS256, jwkKey)) // jwe.Encrypt([]byte(`...`), jwe.WithKey(jwa.RSA_OAEP, jwkKey)) // -// See examples/jwk_parse_example_test.go and other files in the exmaples/ directory for more. +// See examples/jwk_parse_example_test.go and other files in the examples/ directory for more. // // # Advanced Usage: Registering a custom key type and conversion routines // diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwk/ecdsa/BUILD.bazel b/vendor/github.com/lestrrat-go/jwx/v3/jwk/ecdsa/BUILD.bazel index bf058aa649..8490b202a8 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jwk/ecdsa/BUILD.bazel +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwk/ecdsa/BUILD.bazel @@ -1,4 +1,4 @@ -load("@rules_go//go:def.bzl", "go_library") +load("@rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "ecdsa", @@ -13,3 +13,13 @@ alias( actual = ":ecdsa", visibility = ["//visibility:public"], ) + +go_test( + name = "ecdsa_test", + srcs = ["ecdsa_test.go"], + embed = [":ecdsa"], + deps = [ + "//jwa", + "@com_github_stretchr_testify//require", + ], +) diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwk/ecdsa_gen.go b/vendor/github.com/lestrrat-go/jwx/v3/jwk/ecdsa_gen.go index a717e24bb9..301d14dbc0 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jwk/ecdsa_gen.go +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwk/ecdsa_gen.go @@ -734,9 +734,9 @@ func (h *ecdsaPublicKey) MarshalJSON() ([]byte, error) { if i > 0 { buf.WriteByte(tokens.Comma) } - buf.WriteByte('"') - buf.WriteString(pair.Name) - buf.WriteString(`": `) + if err := json.WriteQuotedKey(buf, pair.Name); err != nil { + return nil, fmt.Errorf(`failed to encode field name %q: %w`, pair.Name, err) + } buf.Write(pair.Value.([]byte)) } buf.WriteByte(tokens.CloseCurlyBracket) @@ -1558,9 +1558,9 @@ func (h *ecdsaPrivateKey) MarshalJSON() ([]byte, error) { if i > 0 { buf.WriteByte(tokens.Comma) } - buf.WriteByte('"') - buf.WriteString(pair.Name) - buf.WriteString(`": `) + if err := json.WriteQuotedKey(buf, pair.Name); err != nil { + return nil, fmt.Errorf(`failed to encode field name %q: %w`, pair.Name, err) + } buf.Write(pair.Value.([]byte)) } buf.WriteByte(tokens.CloseCurlyBracket) diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwk/interface.go b/vendor/github.com/lestrrat-go/jwx/v3/jwk/interface.go index db6b8e31d5..27e5ee0fab 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jwk/interface.go +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwk/interface.go @@ -121,12 +121,13 @@ type Set interface { } type set struct { - keys []Key - mu sync.RWMutex - dc DecodeCtx - privateParams map[string]any - maxKeys int // scratch cap consumed by UnmarshalJSON; 0 means use global default - rejectDuplicateKID bool // scratch flag consumed by UnmarshalJSON; false falls back to global + keys []Key + mu sync.RWMutex + dc DecodeCtx + privateParams map[string]any + maxKeys int // scratch cap consumed by UnmarshalJSON; 0 means use global default + rejectDuplicateKID *bool // scratch override consumed by UnmarshalJSON; nil falls back to global + strictKeySetParsing *bool // scratch override consumed by UnmarshalJSON; nil falls back to global } type PublicKeyer interface { diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwk/jwk.go b/vendor/github.com/lestrrat-go/jwx/v3/jwk/jwk.go index ba2db6cb48..3014158bbf 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jwk/jwk.go +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwk/jwk.go @@ -44,8 +44,17 @@ var maxKeys atomic.Int64 // Tunable via WithRejectDuplicateKID / Configure(WithRejectDuplicateKID(...)). var rejectDuplicateKID atomic.Bool +// strictKeySetParsing controls how Parse/UnmarshalJSON treat an entry in +// a JWKS "keys" array that cannot be parsed. Default is true (fail-fast): +// the first unparseable entry fails the whole set, preserving v3's +// historical behavior. When false, the entry is retained as an +// UnsupportedKey placeholder (unless WithIgnoreParseError drops it). +// Tunable via WithStrictKeySetParsing / Configure(WithStrictKeySetParsing(...)). +var strictKeySetParsing atomic.Bool + func init() { maxKeys.Store(1000) + strictKeySetParsing.Store(true) if err := RegisterProbeField(reflect.StructField{ Name: "Kty", @@ -118,12 +127,17 @@ func Import(raw any) (Key, error) { // to remove any fields, if necessary. func PublicSetOf(v Set, options ...PublicSetOption) (Set, error) { var allowSymmetric bool + var omitUnsupported bool for _, option := range options { switch option.Ident() { case identAllowSymmetric{}: if err := option.Value(&allowSymmetric); err != nil { return nil, fmt.Errorf(`failed to retrieve AllowSymmetric option value: %w`, err) } + case identOmitUnsupportedKeys{}: + if err := option.Value(&omitUnsupported); err != nil { + return nil, fmt.Errorf(`failed to retrieve OmitUnsupportedKeys option value: %w`, err) + } } } @@ -135,6 +149,13 @@ func PublicSetOf(v Set, options ...PublicSetOption) (Set, error) { if !ok { return nil, fmt.Errorf(`key not found`) } + if uk, ok := k.(UnsupportedKey); ok { + if omitUnsupported { + continue + } + kid, _ := uk.KeyID() + return nil, fmt.Errorf(`jwk.PublicSetOf: input set contains an unsupported key (kty=%q, kid=%q, index=%d) that could not be parsed; there is no way to prove it holds no private material, so it is not passed through. Pass jwk.WithOmitUnsupportedKeys(true) to drop such entries from the output: %w`, uk.KeyType().String(), kid, i, uk.Reason()) + } if k.KeyType() == jwa.OctetSeq() && !allowSymmetric { kid, _ := k.KeyID() return nil, fmt.Errorf(`jwk.PublicSetOf: input set contains a symmetric key (kid=%q, index=%d); symmetric keys have no public form and would leak secret material if published. Remove symmetric keys from the set before calling PublicSetOf, or pass jwk.WithAllowSymmetric(true) to opt into legacy pass-through behavior`, kid, i) @@ -363,6 +384,7 @@ func Parse(src []byte, options ...ParseOption) (Set, error) { var pemDecoder PEMDecoder maxK := int(maxKeys.Load()) rejectDupKid := rejectDuplicateKID.Load() + strict := strictKeySetParsing.Load() for _, option := range options { switch option.Ident() { case identPEM{}: @@ -394,6 +416,10 @@ func Parse(src []byte, options ...ParseOption) (Set, error) { if err := option.Value(&rejectDupKid); err != nil { return nil, parseerr(`failed to retrieve RejectDuplicateKID option value: %w`, err) } + case identStrictKeySetParsing{}: + if err := option.Value(&strict); err != nil { + return nil, parseerr(`failed to retrieve StrictKeySetParsing option value: %w`, err) + } case identTypedField{}: var pair typedFieldPair // temporary var needed for typed field if err := option.Value(&pair); err != nil { @@ -459,9 +485,20 @@ func Parse(src []byte, options ...ParseOption) (Set, error) { setter.setMaxKeys(maxK) defer setter.setMaxKeys(0) } - if setter, ok := s.(interface{ setRejectDuplicateKID(bool) }); ok && rejectDupKid { - setter.setRejectDuplicateKID(true) - defer setter.setRejectDuplicateKID(false) + // Propagate the resolved reject-duplicate-KID flag. A pointer + // distinguishes "not set by Parse" (nil → Set.UnmarshalJSON uses the + // global default) from an explicit per-call true/false, so a per-call + // false overrides a global true. + if setter, ok := s.(interface{ setRejectDuplicateKID(*bool) }); ok { + setter.setRejectDuplicateKID(&rejectDupKid) + defer setter.setRejectDuplicateKID(nil) + } + // Propagate the resolved strict flag. A pointer distinguishes "not + // set by Parse" (nil → Set.UnmarshalJSON uses the global default of + // true) from an explicit per-call true/false. + if setter, ok := s.(interface{ setStrictKeySetParsing(*bool) }); ok { + setter.setStrictKeySetParsing(&strict) + defer setter.setStrictKeySetParsing(nil) } // Dispatch JWK-vs-JWKS up front. Set.UnmarshalJSON requires JWKS @@ -541,6 +578,11 @@ func ParseString(s string, options ...ParseOption) (Set, error) { // recomputation (for example, when upgrading to a stronger thumbprint hash // via `jwk.WithThumbprintHash`). func AssignKeyID(key Key, options ...AssignKeyIDOption) error { + if uk, ok := key.(UnsupportedKey); ok { + kid, _ := uk.KeyID() + return fmt.Errorf(`jwk.AssignKeyID: cannot assign a key ID to an unsupported key (kty=%q, kid=%q) that could not be parsed; its thumbprint cannot be computed: %w`, uk.KeyType().String(), kid, uk.Reason()) + } + hash := crypto.SHA256 var force bool for _, option := range options { @@ -845,6 +887,12 @@ func Configure(options ...GlobalOption) { continue } rejectDuplicateKID.Store(v) + case identStrictKeySetParsing{}: + var v bool + if err := option.Value(&v); err != nil { + continue + } + strictKeySetParsing.Store(v) } } diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwk/jwkbb/BUILD.bazel b/vendor/github.com/lestrrat-go/jwx/v3/jwk/jwkbb/BUILD.bazel index baf286688a..7c3b89dddf 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jwk/jwkbb/BUILD.bazel +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwk/jwkbb/BUILD.bazel @@ -27,4 +27,4 @@ alias( name = "go_default_library", actual = ":jwkbb", visibility = ["//visibility:public"], -) \ No newline at end of file +) diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwk/okp.go b/vendor/github.com/lestrrat-go/jwx/v3/jwk/okp.go index ddbda60efa..34bad5c153 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jwk/okp.go +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwk/okp.go @@ -40,7 +40,7 @@ func (k *okpPrivateKey) KeyKind() KeyKind { return okpKeyKind(k.Crv) } // Because this is an elliptic curve based Diffie Hellman protocol, it is also referred to // as ECDH. // -// OKP keys are used to represent private/public pairs of thse elliptic curve +// OKP keys are used to represent private/public pairs of these elliptic curve // keys. But note that the name just means Octet Key Pair. func (k *okpPublicKey) Import(rawKeyIf any) error { diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwk/okp_gen.go b/vendor/github.com/lestrrat-go/jwx/v3/jwk/okp_gen.go index 3cfac02757..c6ca49bf99 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jwk/okp_gen.go +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwk/okp_gen.go @@ -684,9 +684,9 @@ func (h *okpPublicKey) MarshalJSON() ([]byte, error) { if i > 0 { buf.WriteByte(tokens.Comma) } - buf.WriteByte('"') - buf.WriteString(pair.Name) - buf.WriteString(`": `) + if err := json.WriteQuotedKey(buf, pair.Name); err != nil { + return nil, fmt.Errorf(`failed to encode field name %q: %w`, pair.Name, err) + } buf.Write(pair.Value.([]byte)) } buf.WriteByte(tokens.CloseCurlyBracket) @@ -1454,9 +1454,9 @@ func (h *okpPrivateKey) MarshalJSON() ([]byte, error) { if i > 0 { buf.WriteByte(tokens.Comma) } - buf.WriteByte('"') - buf.WriteString(pair.Name) - buf.WriteString(`": `) + if err := json.WriteQuotedKey(buf, pair.Name); err != nil { + return nil, fmt.Errorf(`failed to encode field name %q: %w`, pair.Name, err) + } buf.Write(pair.Value.([]byte)) } buf.WriteByte(tokens.CloseCurlyBracket) diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwk/options.yaml b/vendor/github.com/lestrrat-go/jwx/v3/jwk/options.yaml index 765f3ea8e9..91cb0ada89 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jwk/options.yaml +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwk/options.yaml @@ -280,6 +280,39 @@ options: This does not affect `(*Set).AddKey` — programmatic additions remain permissive (AddKey dedupes only by pointer identity). + - ident: StrictKeySetParsing + interface: GlobalParseOption + argument_type: bool + comment: | + WithStrictKeySetParsing controls what happens when an entry in a + JWK Set's "keys" array cannot be parsed. + + In v3 the default is true (strict): the first unparseable entry + fails the entire set, exactly as older v3 releases did. Existing + callers therefore see no change in behavior. + + Pass `WithStrictKeySetParsing(false)` to opt into retention. In + that mode an unparseable entry is neither dropped nor fatal — it + is kept in the set as a `jwk.UnsupportedKey` placeholder that + preserves the entry's original JSON and the error that prevented + parsing (RFC 7517 §5). This lets a set that mixes understood and + not-yet-understood keys (for example, post-quantum keys published + by an identity provider) remain usable for the keys you do + understand. + + Note the cross-version difference: v4 defaults to false (retain), + while v3 defaults to true (fail-fast). The option means the same + thing in both — only the default differs — so call sites that pass + it explicitly are source-compatible across the v3→v4 migration. + + This option is distinct from `WithIgnoreParseError`, which silently + *drops* unparseable entries instead of retaining placeholders. + `WithIgnoreParseError(true)` takes precedence regardless of the + strict setting: the entry is dropped rather than failing the set + or being retained. + + Can be set globally via `jwk.Configure()` or per-call on + `jwk.Parse()` / `jwk.ParseReader()` / `jwk.ParseString()`. - ident: AllowSymmetric interface: PublicSetOption argument_type: bool @@ -296,3 +329,18 @@ options: Pass `WithAllowSymmetric(true)` only if you are certain the resulting set will not be published. When true, symmetric keys are passed through unchanged, matching the legacy behavior. + - ident: OmitUnsupportedKeys + interface: PublicSetOption + argument_type: bool + comment: | + WithOmitUnsupportedKeys controls how `jwk.PublicSetOf` treats + `jwk.UnsupportedKey` placeholders in the input set. + + By default this option is false: a placeholder in the input is an + error, because there is no way to prove that an unparseable entry + contains no private material, and passing it through would risk + republishing a private key. + + Pass `WithOmitUnsupportedKeys(true)` to drop placeholders from the + output set instead. Use this when you intend to publish the public + set and want unparseable entries silently excluded. diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwk/options_gen.go b/vendor/github.com/lestrrat-go/jwx/v3/jwk/options_gen.go index e90a9ee8d4..0121f66bf2 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jwk/options_gen.go +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwk/options_gen.go @@ -213,9 +213,11 @@ type identMaxFetchBodySize struct{} type identMaxKeys struct{} type identMinRSAModulusBits struct{} type identMinRSAPublicExponent struct{} +type identOmitUnsupportedKeys struct{} type identPEM struct{} type identPEMDecoder struct{} type identRejectDuplicateKID struct{} +type identStrictKeySetParsing struct{} type identStrictKeyUsage struct{} type identThumbprintHash struct{} type identWaitReady struct{} @@ -265,6 +267,10 @@ func (identMinRSAPublicExponent) String() string { return "WithMinRSAPublicExponent" } +func (identOmitUnsupportedKeys) String() string { + return "WithOmitUnsupportedKeys" +} + func (identPEM) String() string { return "WithPEM" } @@ -277,6 +283,10 @@ func (identRejectDuplicateKID) String() string { return "WithRejectDuplicateKID" } +func (identStrictKeySetParsing) String() string { + return "WithStrictKeySetParsing" +} + func (identStrictKeyUsage) String() string { return "WithStrictKeyUsage" } @@ -448,6 +458,21 @@ func WithMinRSAPublicExponent(v int) GlobalOption { return &globalOption{option.New(identMinRSAPublicExponent{}, v)} } +// WithOmitUnsupportedKeys controls how `jwk.PublicSetOf` treats +// `jwk.UnsupportedKey` placeholders in the input set. +// +// By default this option is false: a placeholder in the input is an +// error, because there is no way to prove that an unparseable entry +// contains no private material, and passing it through would risk +// republishing a private key. +// +// Pass `WithOmitUnsupportedKeys(true)` to drop placeholders from the +// output set instead. Use this when you intend to publish the public +// set and want unparseable entries silently excluded. +func WithOmitUnsupportedKeys(v bool) PublicSetOption { + return &publicSetOption{option.New(identOmitUnsupportedKeys{}, v)} +} + // WithPEM specifies that the input to `Parse()` is a PEM encoded key. // // This option is planned to be deprecated in the future. The plan is to @@ -488,6 +513,39 @@ func WithRejectDuplicateKID(v bool) GlobalParseOption { return &globalParseOption{option.New(identRejectDuplicateKID{}, v)} } +// WithStrictKeySetParsing controls what happens when an entry in a +// JWK Set's "keys" array cannot be parsed. +// +// In v3 the default is true (strict): the first unparseable entry +// fails the entire set, exactly as older v3 releases did. Existing +// callers therefore see no change in behavior. +// +// Pass `WithStrictKeySetParsing(false)` to opt into retention. In +// that mode an unparseable entry is neither dropped nor fatal — it +// is kept in the set as a `jwk.UnsupportedKey` placeholder that +// preserves the entry's original JSON and the error that prevented +// parsing (RFC 7517 §5). This lets a set that mixes understood and +// not-yet-understood keys (for example, post-quantum keys published +// by an identity provider) remain usable for the keys you do +// understand. +// +// Note the cross-version difference: v4 defaults to false (retain), +// while v3 defaults to true (fail-fast). The option means the same +// thing in both — only the default differs — so call sites that pass +// it explicitly are source-compatible across the v3→v4 migration. +// +// This option is distinct from `WithIgnoreParseError`, which silently +// *drops* unparseable entries instead of retaining placeholders. +// `WithIgnoreParseError(true)` takes precedence regardless of the +// strict setting: the entry is dropped rather than failing the set +// or being retained. +// +// Can be set globally via `jwk.Configure()` or per-call on +// `jwk.Parse()` / `jwk.ParseReader()` / `jwk.ParseString()`. +func WithStrictKeySetParsing(v bool) GlobalParseOption { + return &globalParseOption{option.New(identStrictKeySetParsing{}, v)} +} + // WithStrictKeyUsage specifies if during JWK parsing, the "use" field // should be confined to the values that have been registered via // `jwk.RegisterKeyType()`. By default this option is true, and the diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwk/rsa_gen.go b/vendor/github.com/lestrrat-go/jwx/v3/jwk/rsa_gen.go index 3ef59aec6f..7b44c4e6e7 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jwk/rsa_gen.go +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwk/rsa_gen.go @@ -692,9 +692,9 @@ func (h *rsaPublicKey) MarshalJSON() ([]byte, error) { if i > 0 { buf.WriteByte(tokens.Comma) } - buf.WriteByte('"') - buf.WriteString(pair.Name) - buf.WriteString(`": `) + if err := json.WriteQuotedKey(buf, pair.Name); err != nil { + return nil, fmt.Errorf(`failed to encode field name %q: %w`, pair.Name, err) + } buf.Write(pair.Value.([]byte)) } buf.WriteByte(tokens.CloseCurlyBracket) @@ -1707,9 +1707,9 @@ func (h *rsaPrivateKey) MarshalJSON() ([]byte, error) { if i > 0 { buf.WriteByte(tokens.Comma) } - buf.WriteByte('"') - buf.WriteString(pair.Name) - buf.WriteString(`": `) + if err := json.WriteQuotedKey(buf, pair.Name); err != nil { + return nil, fmt.Errorf(`failed to encode field name %q: %w`, pair.Name, err) + } buf.Write(pair.Value.([]byte)) } buf.WriteByte(tokens.CloseCurlyBracket) diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwk/set.go b/vendor/github.com/lestrrat-go/jwx/v3/jwk/set.go index 6b8c7aa564..498a9b754d 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jwk/set.go +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwk/set.go @@ -211,10 +211,14 @@ func (s *set) setMaxKeys(n int) { s.maxKeys = n } -func (s *set) setRejectDuplicateKID(v bool) { +func (s *set) setRejectDuplicateKID(v *bool) { s.rejectDuplicateKID = v } +func (s *set) setStrictKeySetParsing(v *bool) { + s.strictKeySetParsing = v +} + // UnmarshalJSON streams a JWKS document. The "keys" array is read // element-by-element with the configured cap enforced BEFORE the // (cap+1)-th element is decoded — an attacker-controlled input length @@ -241,7 +245,14 @@ func (s *set) UnmarshalJSON(data []byte) error { if maxK <= 0 { maxK = int(maxKeys.Load()) } - rejectDupKid := s.rejectDuplicateKID || rejectDuplicateKID.Load() + rejectDupKid := rejectDuplicateKID.Load() + if s.rejectDuplicateKID != nil { + rejectDupKid = *s.rejectDuplicateKID + } + strict := strictKeySetParsing.Load() + if s.strictKeySetParsing != nil { + strict = *s.strictKeySetParsing + } dec := json.NewDecoder(bytes.NewReader(data)) LOOP: @@ -285,11 +296,23 @@ LOOP: } key, err := ParseKey(raw, options...) if err != nil { - if !ignoreParseError { + // ignoreParseError is checked first so its + // long-standing "drop the entry" behavior is + // unchanged regardless of the strict flag. Then: + // strict (v3 default) fails the whole set; otherwise + // the entry is retained as an UnsupportedKey + // placeholder (RFC 7517 §5, opt-in via + // WithStrictKeySetParsing(false)). + if ignoreParseError { + i++ + continue + } + if strict { return fmt.Errorf(`failed to decode key #%d in "keys": %w`, i, err) } - i++ - continue + // dec.Decode may reuse its buffer, so + // newUnsupportedKey clones the raw bytes. + key = newUnsupportedKey(raw, err) } if seenKIDs != nil { if kid, ok := key.KeyID(); ok && kid != "" { diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwk/symmetric_gen.go b/vendor/github.com/lestrrat-go/jwx/v3/jwk/symmetric_gen.go index 900ed6537b..389ac9acf8 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jwk/symmetric_gen.go +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwk/symmetric_gen.go @@ -638,9 +638,9 @@ func (h *symmetricKey) MarshalJSON() ([]byte, error) { if i > 0 { buf.WriteByte(tokens.Comma) } - buf.WriteByte('"') - buf.WriteString(pair.Name) - buf.WriteString(`": `) + if err := json.WriteQuotedKey(buf, pair.Name); err != nil { + return nil, fmt.Errorf(`failed to encode field name %q: %w`, pair.Name, err) + } buf.Write(pair.Value.([]byte)) } buf.WriteByte(tokens.CloseCurlyBracket) diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwk/unsupported.go b/vendor/github.com/lestrrat-go/jwx/v3/jwk/unsupported.go new file mode 100644 index 0000000000..57cf035af2 --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwk/unsupported.go @@ -0,0 +1,318 @@ +package jwk + +import ( + "bytes" + "crypto" + "errors" + "fmt" + + "github.com/lestrrat-go/blackmagic" + "github.com/lestrrat-go/jwx/v3/cert" + "github.com/lestrrat-go/jwx/v3/internal/json" + "github.com/lestrrat-go/jwx/v3/jwa" +) + +// UnsupportedKey is a placeholder for a JWK Set entry that could not be +// parsed into a usable key. Per RFC 7517 §5, an entry inside a "keys" +// array whose key type is not understood, that is missing required +// members, or whose values are out of the supported range may be retained +// as an UnsupportedKey instead of failing the whole set. +// +// In v3 retention is opt-in: pass `jwk.WithStrictKeySetParsing(false)` to +// `jwk.Parse` (or set it globally via `jwk.Configure`). By default v3 +// still fails the whole set on the first unparseable entry, so existing +// callers see no change. (In v4 retention is the default; the option +// carries the same meaning in both, only the default differs.) +// +// A placeholder preserves the entry's original JSON — marshaling it with +// json.Marshal (alone or as part of its set) reproduces the entry, so a +// set containing one round-trips losslessly — and it preserves the error +// that prevented parsing (via [UnsupportedKey.Reason]). +// +// An UnsupportedKey cannot be used for any cryptographic operation: +// [UnsupportedKey.Thumbprint], [UnsupportedKey.PublicKey] and +// [UnsupportedKey.Validate] all return an error wrapping Reason(), and +// the key is rejected by cryptographic consumers such as +// jws.Verify / jwe.Decrypt with a descriptive per-key error. +// +// Use [IsUnsupportedKey] to check whether a key is a placeholder. Use a +// type assertion when you also need the placeholder's details: +// +// if uk, ok := key.(jwk.UnsupportedKey); ok { +// // key type key.KeyType() is not supported by this build; +// // uk.Reason() explains why, an extension module may be required. +// } +type UnsupportedKey interface { + Key + + // Reason returns the error that prevented the entry from parsing. + Reason() error + + // isUnsupportedKey seals this interface: only the placeholder type + // produced by this package implements it. Without the seal, any + // third-party Key that happens to define a Reason() error method + // would satisfy UnsupportedKey and be rejected as a placeholder by + // jwk.Export, jwk.AssignKeyID, and jws/jwe key selection. + isUnsupportedKey() +} + +// IsUnsupportedKey reports whether key is a placeholder retained for a +// JWK Set entry that could not be parsed. Only placeholders produced by +// this package satisfy the check; a user-defined Key type can never be +// mistaken for one. It is the sanctioned way to +// skip placeholders when iterating a set; type-assert to +// [UnsupportedKey] when you also need Reason(). +func IsUnsupportedKey(key Key) bool { + _, ok := key.(UnsupportedKey) + return ok +} + +// unsupportedKey is the concrete implementation of [UnsupportedKey]. +// +// It is effectively immutable after construction: the mutators [Set] and +// [Remove] return errors without modifying any field, so no locking is +// required for concurrent reads. The best-effort common members are +// parsed once in [newUnsupportedKey]. +type unsupportedKey struct { + raw []byte + reason error + + rawKty string + ktyPresent bool + algorithm *jwa.KeyAlgorithm + keyID *string +} + +var _ UnsupportedKey = &unsupportedKey{} +var _ Key = &unsupportedKey{} + +// newUnsupportedKey builds a placeholder from the verbatim entry bytes +// and the error that prevented parsing. raw is cloned because the +// decoder buffer it came from may be reused. +func newUnsupportedKey(raw []byte, reason error) *unsupportedKey { + // reason is always non-nil in practice (a placeholder only exists + // because ParseKey failed), but guard anyway so a nil can never + // reach the %w verbs that wrap Reason(). + if reason == nil { + reason = errors.New(`unspecified parse error`) + } + k := &unsupportedKey{ + raw: bytes.Clone(raw), + reason: reason, + } + k.parseBestEffort() + return k +} + +// parseBestEffort re-parses the minimum set of members needed to make +// the placeholder discoverable and nameable: "kid" (LookupKeyID and the +// duplicate-kid check), "kty" (error messages, KeyType()), and "alg" +// (error messages). A member that fails to parse is simply left absent. +// Everything else stays unparsed — the raw JSON is the entry's +// authoritative representation. +func (k *unsupportedKey) parseBestEffort() { + var fields map[string]json.RawMessage + if err := json.Unmarshal(k.raw, &fields); err != nil { + return + } + + if raw, ok := fields[KeyTypeKey]; ok { + var s string + if err := json.Unmarshal(raw, &s); err == nil { + k.rawKty = s + k.ktyPresent = true + } + } + if raw, ok := fields[KeyIDKey]; ok { + var s string + if err := json.Unmarshal(raw, &s); err == nil { + k.keyID = &s + } + } + if raw, ok := fields[AlgorithmKey]; ok { + var s string + if err := json.Unmarshal(raw, &s); err == nil { + if alg, err := jwa.KeyAlgorithmFrom(s); err == nil { + k.algorithm = &alg + } + } + } +} + +func (k *unsupportedKey) Reason() error { + return k.reason +} + +// isUnsupportedKey implements the [UnsupportedKey] interface seal. +func (k *unsupportedKey) isUnsupportedKey() {} + +// unsupportederr wraps the placeholder's Reason() in an error explaining +// that the operation cannot be performed on an unsupported key. +func (k *unsupportedKey) unsupportederr(op string) error { + kid := "" + if k.keyID != nil { + kid = *k.keyID + } + return fmt.Errorf(`jwk: cannot %s an unsupported key (kty=%q, kid=%q): the entry could not be parsed: %w`, op, k.rawKty, kid, k.reason) +} + +func (k *unsupportedKey) KeyType() jwa.KeyType { + if !k.ktyPresent { + return jwa.EmptyKeyType() + } + return jwa.NewKeyType(k.rawKty) +} + +func (k *unsupportedKey) Algorithm() (jwa.KeyAlgorithm, bool) { + if k.algorithm != nil { + return *k.algorithm, true + } + return nil, false +} + +func (k *unsupportedKey) KeyID() (string, bool) { + if k.keyID != nil { + return *k.keyID, true + } + return "", false +} + +// The remaining standard members are not mirrored: nothing consumes them +// on a placeholder (key selection rejects it before ever checking usage), +// and the raw JSON already carries them for round-tripping. + +func (k *unsupportedKey) KeyOps() (KeyOperationList, bool) { + return nil, false +} + +func (k *unsupportedKey) KeyUsage() (string, bool) { + return "", false +} + +func (k *unsupportedKey) X509CertChain() (*cert.Chain, bool) { + return nil, false +} + +func (k *unsupportedKey) X509CertThumbprint() (string, bool) { + return "", false +} + +func (k *unsupportedKey) X509CertThumbprintS256() (string, bool) { + return "", false +} + +func (k *unsupportedKey) X509URL() (string, bool) { + return "", false +} + +func (k *unsupportedKey) Has(name string) bool { + switch name { + case KeyTypeKey: + return k.ktyPresent + case AlgorithmKey: + return k.algorithm != nil + case KeyIDKey: + return k.keyID != nil + default: + return false + } +} + +// Get retrieves the best-effort common members (kty, alg, kid) into dst. +// Any other field is reported as absent — the raw JSON remains its only +// representation. +func (k *unsupportedKey) Get(name string, dst any) error { + switch name { + case KeyTypeKey: + if !k.ktyPresent { + return fmt.Errorf(`field %q not found`, name) + } + return blackmagic.AssignIfCompatible(dst, k.KeyType()) + case AlgorithmKey: + if k.algorithm == nil { + return fmt.Errorf(`field %q not found`, name) + } + return blackmagic.AssignIfCompatible(dst, *k.algorithm) + case KeyIDKey: + if k.keyID == nil { + return fmt.Errorf(`field %q not found`, name) + } + return blackmagic.AssignIfCompatible(dst, *k.keyID) + default: + return fmt.Errorf(`field %q not found`, name) + } +} + +func (k *unsupportedKey) Keys() []string { + keys := make([]string, 0, 3) + if k.ktyPresent { + keys = append(keys, KeyTypeKey) + } + if k.algorithm != nil { + keys = append(keys, AlgorithmKey) + } + if k.keyID != nil { + keys = append(keys, KeyIDKey) + } + return keys +} + +// Set always returns an error: the verbatim raw JSON is the single +// source of truth for serialization, so mutation is not allowed (it +// would make the marshaled form diverge from the accessor view). +func (k *unsupportedKey) Set(string, any) error { + return k.unsupportederr("modify") +} + +// Remove always returns an error, for the same reason as [Set]. +func (k *unsupportedKey) Remove(string) error { + return k.unsupportederr("modify") +} + +// Validate reports the retained parse error: a placeholder is by +// definition not a valid key. The error is wrapped in a key validation +// error so it classifies like every other built-in Key.Validate failure +// (jwk.IsKeyValidationError is true), while Reason() stays reachable +// through the wrapping chain. +func (k *unsupportedKey) Validate() error { + return NewKeyValidationError(k.unsupportederr("validate")) +} + +// Thumbprint always returns an error: RFC 7638 thumbprints require the +// per-kty required members, which are not understood for a placeholder. +func (k *unsupportedKey) Thumbprint(crypto.Hash) ([]byte, error) { + return nil, k.unsupportederr("compute the thumbprint of") +} + +// PublicKey always returns an error: whether the entry contains private +// material is unknowable, so no public projection can be derived safely. +func (k *unsupportedKey) PublicKey() (Key, error) { + return nil, k.unsupportederr("derive the public key of") +} + +// Clone returns an independent copy of the placeholder. Placeholders are +// first-class set members, so they clone like any other key. +func (k *unsupportedKey) Clone() (Key, error) { + dst := &unsupportedKey{ + raw: bytes.Clone(k.raw), + reason: k.reason, + rawKty: k.rawKty, + ktyPresent: k.ktyPresent, + } + if k.algorithm != nil { + tmp := *k.algorithm + dst.algorithm = &tmp + } + if k.keyID != nil { + tmp := *k.keyID + dst.keyID = &tmp + } + return dst, nil +} + +// MarshalJSON emits the verbatim raw JSON of the original entry. This is +// the round-trip guarantee: a set containing a placeholder re-serializes +// the unknown entry unchanged. +func (k *unsupportedKey) MarshalJSON() ([]byte, error) { + return bytes.Clone(k.raw), nil +} diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jws/BUILD.bazel b/vendor/github.com/lestrrat-go/jwx/v3/jws/BUILD.bazel index 32dbdd1881..5f4e5a90d9 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jws/BUILD.bazel +++ b/vendor/github.com/lestrrat-go/jwx/v3/jws/BUILD.bazel @@ -15,9 +15,9 @@ go_library( "message.go", "options.go", "options_gen.go", - "signer.go", "sign_context.go", "signature_builder.go", + "signer.go", "streaming_detached.go", "verifier.go", "verify_context.go", @@ -27,14 +27,14 @@ go_library( deps = [ "//cert", "//internal/base64", - "//internal/ecutil", "//internal/json", - "//internal/tokens", "//internal/keyconv", "//internal/pool", + "//internal/tokens", "//jwa", "//jwk", - "//jws/internal/keytype", + "//jws/internal/jwsbb", + "//jws/internal/keyalg", "//jws/jwsbb", "//jws/legacy", "//transform", @@ -47,25 +47,34 @@ go_library( go_test( name = "jws_test", srcs = [ - "es256k_test.go", + "bench_marshal_test.go", + "bench_serialize_test.go", "filter_test.go", + "format_detect_test.go", + "fuzz_test.go", + "headers_nil_test.go", "headers_test.go", + "jws_crit_test.go", + "jws_internal_test.go", "jws_test.go", + "key_provider_test.go", "message_test.go", "options_gen_test.go", "signer_test.go", "streaming_detached_test.go", + "unsupported_key_test.go", ], embed = [":jws"], deps = [ "//cert", "//internal/base64", - "//internal/ecutil", "//internal/json", "//internal/jwxtest", + "//internal/tokens", "//jwa", "//jwk", - "//jwt", + "//jws/legacy", + "@com_github_lestrrat_go_dsig//:dsig", "@com_github_lestrrat_go_httprc_v3//:httprc", "@com_github_stretchr_testify//require", ], diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jws/errors.go b/vendor/github.com/lestrrat-go/jwx/v3/jws/errors.go index e4445bd547..91577a014b 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jws/errors.go +++ b/vendor/github.com/lestrrat-go/jwx/v3/jws/errors.go @@ -3,6 +3,8 @@ package jws import ( "errors" "fmt" + + "github.com/lestrrat-go/jwx/v3/jws/internal/keyalg" ) // errCritPresent is returned by VerifyCompactFast when the protected @@ -45,25 +47,17 @@ func ErrB64Present() error { return errB64Present } -// errUnclassifiableKey is the common sentinel for AlgorithmsForKey -// failures: the key shape cannot be matched to any registered key type -// for signing. Three different code paths land here — Import-failed, -// kty-not-registered, and shape-rejected (e.g. ecdh) — but they're all -// the same logical "we can't classify this key" outcome from the -// caller's perspective. Wrap-with-this lets callers branch on -// errors.Is(err, jws.ErrUnclassifiableKey()) instead of pattern-matching -// the three error-message shapes the function previously emitted. -var errUnclassifiableKey = errors.New("jws: key cannot be classified for signing") - -// ErrUnclassifiableKey returns the sentinel that jws.AlgorithmsForKey -// (and indirectly jws.Sign / jws.Verify when option-time validation -// fails) wraps when the supplied key cannot be matched to a registered -// key type. Branching on this sentinel is the right way to ask "is this -// a 'we can't tell what this key is' failure?" — the wrapping error -// also carries the concrete %T or %q diagnostic in its message, so the -// human-readable error stays specific. +// ErrUnclassifiableKey returns the sentinel that jws.Sign and jws.Verify +// wrap when option-time validation cannot match the supplied key to a +// registered key type. Branching on this sentinel is the right way to ask +// "is this a 'we can't tell what this key is' failure?" — the wrapping +// error also carries the concrete %T or %q diagnostic in its message, so +// the human-readable error stays specific. +// +// The sentinel itself lives in jws/internal/keyalg, which owns key +// classification. func ErrUnclassifiableKey() error { - return errUnclassifiableKey + return keyalg.ErrUnclassifiableKey } type signError struct { diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jws/headers_gen.go b/vendor/github.com/lestrrat-go/jwx/v3/jws/headers_gen.go index 0628e626d2..04cf66909f 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jws/headers_gen.go +++ b/vendor/github.com/lestrrat-go/jwx/v3/jws/headers_gen.go @@ -812,9 +812,9 @@ func (h *stdHeaders) MarshalJSON() ([]byte, error) { if i > 0 { buf.WriteByte(tokens.Comma) } - buf.WriteByte('"') - buf.WriteString(pair.Name) - buf.WriteString(`": `) + if err := json.WriteQuotedKey(buf, pair.Name); err != nil { + return nil, fmt.Errorf(`failed to encode field name %q: %w`, pair.Name, err) + } buf.Write(pair.Value.([]byte)) } buf.WriteByte(tokens.CloseCurlyBracket) diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jws/internal/jwsbb/BUILD.bazel b/vendor/github.com/lestrrat-go/jwx/v3/jws/internal/jwsbb/BUILD.bazel new file mode 100644 index 0000000000..b762d41a4b --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v3/jws/internal/jwsbb/BUILD.bazel @@ -0,0 +1,25 @@ +load("@rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "jwsbb", + srcs = ["ecdsacurve.go"], + importpath = "github.com/lestrrat-go/jwx/v3/jws/internal/jwsbb", + visibility = ["//jws:__subpackages__"], + deps = ["@com_github_lestrrat_go_dsig//:dsig"], +) + +alias( + name = "go_default_library", + actual = ":jwsbb", + visibility = ["//jws:__subpackages__"], +) + +go_test( + name = "jwsbb_test", + srcs = ["ecdsacurve_test.go"], + deps = [ + ":jwsbb", + "@com_github_lestrrat_go_dsig//:dsig", + "@com_github_stretchr_testify//require", + ], +) diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jws/internal/jwsbb/ecdsacurve.go b/vendor/github.com/lestrrat-go/jwx/v3/jws/internal/jwsbb/ecdsacurve.go new file mode 100644 index 0000000000..c8715e6c6d --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v3/jws/internal/jwsbb/ecdsacurve.go @@ -0,0 +1,117 @@ +package jwsbb + +import ( + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "fmt" + + "github.com/lestrrat-go/dsig" +) + +// This file enforces the RFC 7518 Section 3.4 binding between an ECDSA JWS +// algorithm and the curve its key must sit on (ES256/P-256, ES384/P-384, +// ES512/P-521). It is sign-side only, and jws reaches it only when the caller +// passes jws.WithStrictECDSA(true). +// +// The check is opt-in because the old permissive behavior is an interop +// defect, not a security hole: the signer controls both the key and the +// algorithm at the call site, and the JWS it produces is a genuine signature +// under its own key. Turning the check on by default would break working +// callers to fix a conformance problem they may not have. +// +// jws.Verify never reaches this file at all. It infers algorithms from a key +// when a JWKS entry carries no "alg" (see jws/internal/keyalg.Candidates and +// the deprecated jws.AlgorithmsForKey, whose godoc freezes that inference), +// and it must stay exactly as permissive as it is today. + +// RequireECDSACurve reports whether key sits on the curve RFC 7518 Section +// 3.4 binds joseAlg to. It returns nil -- never an error -- when the binding +// cannot be established: dsigAlg is an ECDSA-family algorithm outside the +// three JOSE built-ins (e.g. ES256K, whether from the jwx_es256k build tag +// or an extension module), or key carries no readable curve. Only positive +// evidence of a mismatch is an error. +func RequireECDSACurve(joseAlg, dsigAlg string, key any) error { + want, ok := curveForDsigAlgorithm(dsigAlg) + if !ok { + return nil + } + + pub := ecdsaPublicKeyOf(key) + if pub == nil || pub.Curve == nil { + return nil + } + + if pub.Curve == want { + return nil + } + gotParams := pub.Curve.Params() + if gotParams == nil { + return nil + } + wantParams := want.Params() + if wantParams != nil && gotParams.Name == wantParams.Name { + return nil + } + + return fmt.Errorf(`ECDSA curve mismatch: key is on %s, algorithm %q requires %s`, + curveName(pub.Curve), joseAlg, curveName(want)) +} + +// curveForDsigAlgorithm maps a dsig ECDSA algorithm name to the curve RFC +// 7518 Section 3.4 requires for it. Only the three JOSE built-ins are +// known; anything else (custom-curve extensions such as ES256K) misses +// deliberately, so the caller passes the key through unchecked. +func curveForDsigAlgorithm(dsigAlg string) (elliptic.Curve, bool) { + switch dsigAlg { + case dsig.ECDSAWithP256AndSHA256: + return elliptic.P256(), true + case dsig.ECDSAWithP384AndSHA384: + return elliptic.P384(), true + case dsig.ECDSAWithP521AndSHA512: + return elliptic.P521(), true + default: + return nil, false + } +} + +// ecdsaPublicKeyOf extracts an *ecdsa.PublicKey from key, or nil when key is +// not (or does not expose) an ECDSA key. Callers pass an already-converted +// key (jwk.Key unwrapping happens before this is called), so only the raw Go +// crypto forms and an opaque crypto.Signer are handled here. +func ecdsaPublicKeyOf(key any) *ecdsa.PublicKey { + switch k := key.(type) { + case *ecdsa.PrivateKey: + if k == nil { + return nil + } + return &k.PublicKey + case ecdsa.PrivateKey: + return &k.PublicKey + case *ecdsa.PublicKey: + return k + case ecdsa.PublicKey: + return &k + case crypto.Signer: + pub, ok := k.Public().(*ecdsa.PublicKey) + if !ok { + return nil + } + return pub + default: + return nil + } +} + +// curveName returns crv.Params().Name, guarding a nil Params() the same way +// the comparison in RequireECDSACurve does. +func curveName(crv elliptic.Curve) string { + if crv == nil { + return "" + } + params := crv.Params() + if params == nil { + return "" + } + return params.Name +} diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jws/internal/keyalg/BUILD.bazel b/vendor/github.com/lestrrat-go/jwx/v3/jws/internal/keyalg/BUILD.bazel new file mode 100644 index 0000000000..97313cd6ca --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v3/jws/internal/keyalg/BUILD.bazel @@ -0,0 +1,18 @@ +load("@rules_go//go:def.bzl", "go_library") + +go_library( + name = "keyalg", + srcs = ["keyalg.go"], + importpath = "github.com/lestrrat-go/jwx/v3/jws/internal/keyalg", + visibility = ["//jws:__subpackages__"], + deps = [ + "//jwa", + "//jwk", + ], +) + +alias( + name = "go_default_library", + actual = ":keyalg", + visibility = ["//jws:__subpackages__"], +) diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jws/internal/keyalg/keyalg.go b/vendor/github.com/lestrrat-go/jwx/v3/jws/internal/keyalg/keyalg.go new file mode 100644 index 0000000000..09458230bc --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v3/jws/internal/keyalg/keyalg.go @@ -0,0 +1,259 @@ +// Package keyalg works out which signature algorithms a key can be used +// with, and owns the registration tables it reads to decide. +// +// The answer is a guess, on purpose. jws.Verify uses it to pick +// algorithms to try when a JWKS key has no "alg" field, and option +// handling uses it to catch a key that clearly does not go with the +// algorithm asked for. It is not a check for whether a key and an +// algorithm are a valid pair, and the list can be wider than any one RFC +// allows for a given key. +// +// This package is internal to jwx. The jws package still has +// AlgorithmsForKey, a one-line wrapper over [Candidates], but that is +// deprecated and was never meant for callers outside jwx. Everything in +// the tree calls this package instead. +package keyalg + +import ( + "crypto" + "crypto/ecdh" + "crypto/ecdsa" + "crypto/ed25519" + "crypto/rsa" + "errors" + "fmt" + "slices" + "sync" + + "github.com/lestrrat-go/jwx/v3/jwa" + "github.com/lestrrat-go/jwx/v3/jwk" +) + +// ErrUnclassifiableKey is the common sentinel for [Candidates] failures: +// the key shape cannot be matched to any registered key type for signing. +// Three different code paths land here — Import-failed, kty-not-registered, +// and shape-rejected (e.g. ecdh) — but they're all the same logical "we +// can't classify this key" outcome from the caller's perspective. +// Wrap-with-this lets callers branch on errors.Is instead of +// pattern-matching the three error-message shapes. +// +// The jws package re-exports this through jws.ErrUnclassifiableKey(). +var ErrUnclassifiableKey = errors.New("jws: key cannot be classified for signing") + +// curver is implemented by jwk.Key types that carry curve information. +type curver interface { + Crv() (jwa.EllipticCurveAlgorithm, bool) +} + +var mu sync.RWMutex +var keyTypeToAlgorithms = make(map[jwa.KeyType][]jwa.SignatureAlgorithm) +var algorithmToKeyTypes = make(map[jwa.SignatureAlgorithm][]jwa.KeyType) +var curveToAlgorithms = make(map[jwa.EllipticCurveAlgorithm][]jwa.SignatureAlgorithm) + +func init() { + RegisterForKeyType(jwa.OKP(), jwa.EdDSA()) + RegisterForCurve(jwa.Ed25519(), jwa.EdDSAEd25519()) + for _, alg := range []jwa.SignatureAlgorithm{jwa.HS256(), jwa.HS384(), jwa.HS512()} { + RegisterForKeyType(jwa.OctetSeq(), alg) + } + for _, alg := range []jwa.SignatureAlgorithm{jwa.RS256(), jwa.RS384(), jwa.RS512(), jwa.PS256(), jwa.PS384(), jwa.PS512()} { + RegisterForKeyType(jwa.RSA(), alg) + } + for _, alg := range []jwa.SignatureAlgorithm{jwa.ES256(), jwa.ES384(), jwa.ES512()} { + RegisterForKeyType(jwa.EC(), alg) + } +} + +// RegisterForKeyType records alg as usable with keys of type kty. +// +// This backs jws.RegisterAlgorithmForKeyType, which extension modules +// call from init() to add their own algorithms. +func RegisterForKeyType(kty jwa.KeyType, alg jwa.SignatureAlgorithm) { + mu.Lock() + defer mu.Unlock() + keyTypeToAlgorithms[kty] = append(keyTypeToAlgorithms[kty], alg) + if !slices.Contains(algorithmToKeyTypes[alg], kty) { + algorithmToKeyTypes[alg] = append(algorithmToKeyTypes[alg], kty) + } +} + +// RegisterForCurve scopes alg to the given elliptic curve. When +// [Candidates] can determine a key's curve, an algorithm registered under +// some curve is offered only for keys on that curve, instead of for every +// key of its key type. +// +// This backs jws.RegisterAlgorithmForCurve. It is append-only and +// deduplicates entries, so builtin registrations cannot be overwritten by +// external modules. +func RegisterForCurve(crv jwa.EllipticCurveAlgorithm, alg jwa.SignatureAlgorithm) { + mu.Lock() + defer mu.Unlock() + if slices.Contains(curveToAlgorithms[crv], alg) { + return + } + curveToAlgorithms[crv] = append(curveToAlgorithms[crv], alg) +} + +// KeyTypesFor returns the key types registered for alg. The reverse index +// is maintained at registration time so this is an O(1) lookup. It returns +// nil if no key type is registered for alg, which signals callers to skip +// any prefilter. +func KeyTypesFor(alg jwa.SignatureAlgorithm) []jwa.KeyType { + mu.RLock() + defer mu.RUnlock() + // Copy so the caller can safely iterate without holding the lock; + // RegisterForKeyType may append concurrently after we return. + // Typical length is 1. + return slices.Clone(algorithmToKeyTypes[alg]) +} + +// Candidates returns the signature algorithms that key could be used +// with. It only takes into consideration keys/algorithms for verification +// purposes, as this is the only usage where one may need to dynamically +// figure out which method to use. +// +// When the key's curve is known, algorithms registered for that curve via +// [RegisterForCurve] are combined with key-type-level algorithms to +// produce a more precise result. The curve is known for a [jwk.Key] that +// has a Crv() method, for raw ed25519 keys, and for any raw key that +// reaches the [jwk.Import] fallback below. +// +// ECDSA is the exception. A raw [ecdsa.PublicKey] or [ecdsa.PrivateKey] is +// classified by key type alone and its Curve field is never read. No +// builtin registration binds P-256, P-384, or P-521 to an algorithm +// either, so every EC key reports the full ES* list no matter which curve +// it sits on. RFC 7518 Section 3.4 is stricter than that; jws.Sign +// enforces it only when the caller passes jws.WithStrictECDSA(true). +// +// Accepted key shapes (resolved in order): +// +// 1. [jwk.Key] — kty is read directly; if the implementation also exposes +// Crv(), the curve refines the result. +// 2. Stdlib crypto types: [rsa.PublicKey] / [rsa.PrivateKey] (and pointer +// forms), [ecdsa.PublicKey] / [ecdsa.PrivateKey] (and pointer forms), +// [ed25519.PublicKey], [ed25519.PrivateKey], and [byte] slices for +// symmetric keys. +// 3. [crypto/ecdh.PublicKey] / [crypto/ecdh.PrivateKey] (and pointer +// forms) — explicitly rejected; ECDH keys are key-agreement only. +// Returns an error wrapping [ErrUnclassifiableKey]. +// 4. [crypto.Signer] (e.g. KMS-backed adapters) — resolved once via +// .Public(); the public key is then re-classified through tiers 1–2 +// or the [jwk.Import] fallback below. To prevent infinite recursion, +// a Signer whose .Public() is itself a Signer is left for the +// downstream dispatcher to handle. +// 5. [jwk.Import] fallback — anything else is offered to the import +// registry, allowing extension modules to register their own raw key +// types. +// +// All "we cannot classify this key" failures wrap [ErrUnclassifiableKey], +// so callers can branch with errors.Is rather than pattern-matching error +// strings. The wrapping error keeps the concrete %T or %q diagnostic in +// its message for human readers. +func Candidates(key any) ([]jwa.SignatureAlgorithm, error) { + var kty jwa.KeyType + var crv jwa.EllipticCurveAlgorithm + var hasCrv bool + + switch key := key.(type) { + case jwk.Key: + kty = key.KeyType() + if ck, ok := key.(curver); ok { + crv, hasCrv = ck.Crv() + } + case rsa.PublicKey, *rsa.PublicKey, rsa.PrivateKey, *rsa.PrivateKey: + kty = jwa.RSA() + case ecdsa.PublicKey, *ecdsa.PublicKey, ecdsa.PrivateKey, *ecdsa.PrivateKey: + kty = jwa.EC() + case ed25519.PublicKey, ed25519.PrivateKey: + kty = jwa.OKP() + crv = jwa.Ed25519() + hasCrv = true + case *ecdh.PublicKey, ecdh.PublicKey, *ecdh.PrivateKey, ecdh.PrivateKey: + // ecdh keys are for key agreement (X25519/X448), not signing. + // Reject at the API boundary instead of returning a misleading + // algorithm list that would fail deeper in the signing stack. + return nil, fmt.Errorf(`%w: key type %T cannot be used for signing (ecdh keys are key-agreement only)`, ErrUnclassifiableKey, key) + case []byte: + kty = jwa.OctetSeq() + default: + // For crypto.Signer from external packages (e.g. KMS-backed signers), + // extract the underlying public key type via .Public(). + // Standard library types (*rsa.PrivateKey, etc.) are already handled + // by the concrete cases above. + var signerPubErr error + if signer, ok := key.(crypto.Signer); ok { + pub := signer.Public() + // Guard: only recurse if the public key is not itself a crypto.Signer, + // to prevent infinite recursion from pathological implementations. + if _, isSigner := pub.(crypto.Signer); !isSigner { + algs, err := Candidates(pub) + if err == nil { + return algs, nil + } + // Save the inner classification error so a + // downstream Import-fallback failure can surface + // both diagnostics. A successful Import discards + // signerPubErr — only the eventual failure path + // joins them. + signerPubErr = err + } + } + imported, err := jwk.Import(key) + if err != nil { + outer := fmt.Errorf(`%w: unknown key type %T`, ErrUnclassifiableKey, key) + if signerPubErr != nil { + return nil, errors.Join(outer, signerPubErr) + } + return nil, outer + } + kty = imported.KeyType() + if ck, ok := imported.(curver); ok { + crv, hasCrv = ck.Crv() + } + } + + mu.RLock() + defer mu.RUnlock() + + ktyAlgs, ok := keyTypeToAlgorithms[kty] + if !ok { + return nil, fmt.Errorf(`%w: unregistered key type %q`, ErrUnclassifiableKey, kty) + } + + // If we know the curve and there are curve-specific registrations, + // return only key-type-level algorithms (those not registered under + // any curve) plus curve-specific algorithms for this curve. + if hasCrv { + crvAlgs := curveToAlgorithms[crv] + return filterForCurve(ktyAlgs, crvAlgs), nil + } + + return ktyAlgs, nil +} + +// filterForCurve returns the subset of ktyAlgs that are not registered +// under any curve (i.e., generic for the key type) plus the curve-specific +// algorithms from crvAlgs. +func filterForCurve(ktyAlgs, crvAlgs []jwa.SignatureAlgorithm) []jwa.SignatureAlgorithm { + var result []jwa.SignatureAlgorithm + + // Add key-type-level algorithms that are not claimed by any curve + for _, alg := range ktyAlgs { + if !isRegisteredUnderAnyCurve(alg) { + result = append(result, alg) + } + } + + // Add curve-specific algorithms + result = append(result, crvAlgs...) + return result +} + +func isRegisteredUnderAnyCurve(alg jwa.SignatureAlgorithm) bool { + for _, algs := range curveToAlgorithms { + if slices.Contains(algs, alg) { + return true + } + } + return false +} diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jws/internal/keytype/BUILD.bazel b/vendor/github.com/lestrrat-go/jwx/v3/jws/internal/keytype/BUILD.bazel index eb8bd94acb..f43eec37ac 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jws/internal/keytype/BUILD.bazel +++ b/vendor/github.com/lestrrat-go/jwx/v3/jws/internal/keytype/BUILD.bazel @@ -9,3 +9,9 @@ go_library( "//jwk", ], ) + +alias( + name = "go_default_library", + actual = ":keytype", + visibility = ["//jws:__subpackages__"], +) diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jws/jws.go b/vendor/github.com/lestrrat-go/jwx/v3/jws/jws.go index 99bf78581a..90fc2d4c47 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jws/jws.go +++ b/vendor/github.com/lestrrat-go/jwx/v3/jws/jws.go @@ -27,11 +27,6 @@ package jws import ( "crypto" - "crypto/ecdh" - "crypto/ecdsa" - "crypto/ed25519" - "crypto/rsa" - "errors" "fmt" "io" "slices" @@ -46,6 +41,7 @@ import ( "github.com/lestrrat-go/jwx/v3/internal/tokens" "github.com/lestrrat-go/jwx/v3/jwa" "github.com/lestrrat-go/jwx/v3/jwk" + "github.com/lestrrat-go/jwx/v3/jws/internal/keyalg" "github.com/lestrrat-go/jwx/v3/jws/jwsbb" ) @@ -535,57 +531,29 @@ func RegisterCustomField(name string, object any) { registry.Register(name, object) } -// curver is implemented by jwk.Key types that carry curve information. -type curver interface { - Crv() (jwa.EllipticCurveAlgorithm, bool) -} - -// Helpers for signature verification -var muAlgorithmMaps sync.RWMutex -var keyTypeToAlgorithms = make(map[jwa.KeyType][]jwa.SignatureAlgorithm) -var algorithmToKeyTypes = make(map[jwa.SignatureAlgorithm][]jwa.KeyType) -var curveToAlgorithms = make(map[jwa.EllipticCurveAlgorithm][]jwa.SignatureAlgorithm) - -func init() { - RegisterAlgorithmForKeyType(jwa.OKP(), jwa.EdDSA()) - RegisterAlgorithmForCurve(jwa.Ed25519(), jwa.EdDSAEd25519()) - for _, alg := range []jwa.SignatureAlgorithm{jwa.HS256(), jwa.HS384(), jwa.HS512()} { - RegisterAlgorithmForKeyType(jwa.OctetSeq(), alg) - } - for _, alg := range []jwa.SignatureAlgorithm{jwa.RS256(), jwa.RS384(), jwa.RS512(), jwa.PS256(), jwa.PS384(), jwa.PS512()} { - RegisterAlgorithmForKeyType(jwa.RSA(), alg) - } - for _, alg := range []jwa.SignatureAlgorithm{jwa.ES256(), jwa.ES384(), jwa.ES512()} { - RegisterAlgorithmForKeyType(jwa.EC(), alg) - } -} - // RegisterAlgorithmForKeyType registers an additional algorithm as valid for -// the given key type. This is used internally by init() and can also be called -// from external modules that provide support for additional algorithms (e.g. Ed448). +// the given key type. This is used internally to register the builtin +// algorithms, and can also be called from external modules that provide +// support for additional algorithms (e.g. Ed448). +// +// Registering an algorithm here makes [Sign] and [Verify] accept it for keys +// of that type, and makes it a candidate when a JWKS key carrying no "alg" +// member is verified under jws.WithInferAlgorithmFromKey(true). func RegisterAlgorithmForKeyType(kty jwa.KeyType, alg jwa.SignatureAlgorithm) { - muAlgorithmMaps.Lock() - defer muAlgorithmMaps.Unlock() - keyTypeToAlgorithms[kty] = append(keyTypeToAlgorithms[kty], alg) - if !slices.Contains(algorithmToKeyTypes[alg], kty) { - algorithmToKeyTypes[alg] = append(algorithmToKeyTypes[alg], kty) - } + keyalg.RegisterForKeyType(kty, alg) } -// RegisterAlgorithmForCurve registers an algorithm as valid for the given -// elliptic curve. When [AlgorithmsForKey] can determine the curve of a key, -// it returns the union of key-type-level algorithms and curve-specific -// algorithms instead of all algorithms for the key type. +// RegisterAlgorithmForCurve scopes an algorithm to the given elliptic curve. +// When the curve of a key can be determined, an algorithm registered under +// some curve is offered only for keys on that curve, instead of for every key +// of its key type. Pair this with [RegisterAlgorithmForKeyType] so that, for +// example, an OKP algorithm meant for one curve does not become a candidate +// for every OKP key. // // This function is append-only and deduplicates entries, so builtin // registrations cannot be overwritten by external modules. func RegisterAlgorithmForCurve(crv jwa.EllipticCurveAlgorithm, alg jwa.SignatureAlgorithm) { - muAlgorithmMaps.Lock() - defer muAlgorithmMaps.Unlock() - if slices.Contains(curveToAlgorithms[crv], alg) { - return - } - curveToAlgorithms[crv] = append(curveToAlgorithms[crv], alg) + keyalg.RegisterForCurve(crv, alg) } // AlgorithmsForKey returns the possible signature algorithms that can @@ -593,10 +561,18 @@ func RegisterAlgorithmForCurve(crv jwa.EllipticCurveAlgorithm, alg jwa.Signature // for verification purposes, as this is the only usage where one may need // dynamically figure out which method to use. // -// When the key's curve can be determined (via [jwk.Key] Crv() method or -// inferred from the raw Go type), curve-specific algorithms registered via +// When the key's curve is known, algorithms registered for that curve via // [RegisterAlgorithmForCurve] are combined with key-type-level algorithms -// to produce a more precise result. +// to produce a more precise result. The curve is known for a [jwk.Key] +// that has a Crv() method, for raw ed25519 keys, and for any raw key that +// reaches the [jwk.Import] fallback below. +// +// ECDSA is the exception. A raw [ecdsa.PublicKey] or [ecdsa.PrivateKey] is +// classified by key type alone and its Curve field is never read. No +// builtin registration binds P-256, P-384, or P-521 to an algorithm +// either, so every EC key reports the full ES* list no matter which curve +// it sits on. RFC 7518 Section 3.4 is stricter than that; see +// [WithStrictECDSA] for enforcing it when signing. // // Accepted key shapes (resolved in order): // @@ -622,130 +598,66 @@ func RegisterAlgorithmForCurve(crv jwa.EllipticCurveAlgorithm, alg jwa.Signature // so callers can branch with errors.Is rather than pattern-matching error // strings. The wrapping error keeps the concrete %T or %q diagnostic in // its message for human readers. +// +// Deprecated: Do not use. This is an internal helper that jwx uses to +// guess which algorithms to try when a JWKS key has no "alg" field. It is +// exported only because it always has been, and was never meant for +// callers outside jwx. It does not tell you whether a key and an +// algorithm go together, so do not use it as that kind of check. The list +// it hands back can be wider than RFC 7518 allows for the key you passed. +// +// It keeps working for the rest of the v3 series, and is deprecated in v4 +// as well. It will not be fixed in the meantime, and the way it picks +// algorithms will not change. The list itself can still grow. An +// extension module that calls [RegisterAlgorithmForKeyType] or +// [RegisterAlgorithmForCurve] adds to what this reports, the same way it +// adds to what [Sign] and [Verify] accept. +// +// To find out whether a key works with an algorithm, pass both to [Sign] +// or [Verify] and check the error. func AlgorithmsForKey(key any) ([]jwa.SignatureAlgorithm, error) { - var kty jwa.KeyType - var crv jwa.EllipticCurveAlgorithm - var hasCrv bool - - switch key := key.(type) { - case jwk.Key: - kty = key.KeyType() - if ck, ok := key.(curver); ok { - crv, hasCrv = ck.Crv() - } - case rsa.PublicKey, *rsa.PublicKey, rsa.PrivateKey, *rsa.PrivateKey: - kty = jwa.RSA() - case ecdsa.PublicKey, *ecdsa.PublicKey, ecdsa.PrivateKey, *ecdsa.PrivateKey: - kty = jwa.EC() - case ed25519.PublicKey, ed25519.PrivateKey: - kty = jwa.OKP() - crv = jwa.Ed25519() - hasCrv = true - case *ecdh.PublicKey, ecdh.PublicKey, *ecdh.PrivateKey, ecdh.PrivateKey: - // ecdh keys are for key agreement (X25519/X448), not signing. - // Reject at the API boundary instead of returning a misleading - // algorithm list that would fail deeper in the signing stack. - return nil, fmt.Errorf(`%w: key type %T cannot be used for signing (ecdh keys are key-agreement only)`, errUnclassifiableKey, key) - case []byte: - kty = jwa.OctetSeq() - default: - // For crypto.Signer from external packages (e.g. KMS-backed signers), - // extract the underlying public key type via .Public(). - // Standard library types (*rsa.PrivateKey, etc.) are already handled - // by the concrete cases above. - var signerPubErr error - if signer, ok := key.(crypto.Signer); ok { - pub := signer.Public() - // Guard: only recurse if the public key is not itself a crypto.Signer, - // to prevent infinite recursion from pathological implementations. - if _, isSigner := pub.(crypto.Signer); !isSigner { - algs, err := AlgorithmsForKey(pub) - if err == nil { - return algs, nil - } - // Save the inner classification error so a - // downstream Import-fallback failure can surface - // both diagnostics. A successful Import discards - // signerPubErr — only the eventual failure path - // joins them. - signerPubErr = err - } - } - imported, err := jwk.Import(key) - if err != nil { - outer := fmt.Errorf(`%w: unknown key type %T`, errUnclassifiableKey, key) - if signerPubErr != nil { - return nil, errors.Join(outer, signerPubErr) - } - return nil, outer - } - kty = imported.KeyType() - if ck, ok := imported.(curver); ok { - crv, hasCrv = ck.Crv() - } - } - - muAlgorithmMaps.RLock() - defer muAlgorithmMaps.RUnlock() - - ktyAlgs, ok := keyTypeToAlgorithms[kty] - if !ok { - return nil, fmt.Errorf(`%w: unregistered key type %q`, errUnclassifiableKey, kty) - } - - // If we know the curve and there are curve-specific registrations, - // return only key-type-level algorithms (those not registered under - // any curve) plus curve-specific algorithms for this curve. - if hasCrv { - crvAlgs := curveToAlgorithms[crv] - return filterAlgorithmsForCurve(ktyAlgs, crvAlgs), nil - } - - return ktyAlgs, nil -} - -// filterAlgorithmsForCurve returns the subset of ktyAlgs that are not -// registered under any curve (i.e., generic for the key type) plus the -// curve-specific algorithms from crvAlgs. -func filterAlgorithmsForCurve(ktyAlgs, crvAlgs []jwa.SignatureAlgorithm) []jwa.SignatureAlgorithm { - var result []jwa.SignatureAlgorithm - - // Add key-type-level algorithms that are not claimed by any curve - for _, alg := range ktyAlgs { - if !isRegisteredUnderAnyCurve(alg) { - result = append(result, alg) - } - } - - // Add curve-specific algorithms - result = append(result, crvAlgs...) - return result -} - -func isRegisteredUnderAnyCurve(alg jwa.SignatureAlgorithm) bool { - for _, algs := range curveToAlgorithms { - if slices.Contains(algs, alg) { - return true - } - } - return false + // The godoc says the way this picks algorithms will not change, so + // calling keyalg only works while keyalg picks them the same way this + // function did before it was deprecated. It does today. If Candidates + // ever changes (narrowing EC keys to the one algorithm their curve + // allows is the likely first case), copy the old code back in here + // instead of letting the change through. An extension registering a + // new algorithm is not that kind of change, because the tables have + // always been an input. + return keyalg.Candidates(key) +} + +// unsupportedKeyError builds the rejection error for a jwk.UnsupportedKey +// placeholder that reached a cryptographic entry point. op names the +// operation the placeholder cannot perform (e.g. "signature verification"). +// The error names the placeholder's kid and kty, and wraps the retained +// parse error from Reason(). +func unsupportedKeyError(uk jwk.UnsupportedKey, op string) error { + kid, _ := uk.KeyID() + return fmt.Errorf(`key with kid %q has unsupported key type %q and cannot be used for %s; an extension module may be required to parse it: %w`, kid, uk.KeyType().String(), op, uk.Reason()) } // validateAlgorithmForKey checks that alg is compatible with key. +// A jwk.UnsupportedKey placeholder is rejected up front — before any of +// the carve-outs below — because it carries no usable key material for +// any algorithm, custom or built-in. // Three classification failures are intentionally allowed through: // (a) a nil key, used by keyless algorithms (see GH910); // (b) any key handed to an algorithm with a user-registered custom // Signer2/Verifier2 — custom implementations may accept arbitrary key -// types that AlgorithmsForKey cannot classify; and +// types that keyalg.Candidates cannot classify; and // (c) an opaque crypto.Signer whose .Public() is itself a crypto.Signer, -// the one case AlgorithmsForKey refuses to recurse into. +// the one case keyalg.Candidates refuses to recurse into. // Every other classification failure is surfaced so callers get a crisp // option-boundary rejection instead of a deep-stack error. func validateAlgorithmForKey(alg jwa.SignatureAlgorithm, key any) error { + if uk, ok := key.(jwk.UnsupportedKey); ok { + return fmt.Errorf(`jws.WithKey: %w`, unsupportedKeyError(uk, `signing or signature verification`)) + } if key == nil { return nil } - algs, err := AlgorithmsForKey(key) + algs, err := keyalg.Candidates(key) if err != nil { if hasCustomSigVerifier(alg) { return nil diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jws/jwsbb/BUILD.bazel b/vendor/github.com/lestrrat-go/jwx/v3/jws/jwsbb/BUILD.bazel index 54e64265a0..ca0b963032 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jws/jwsbb/BUILD.bazel +++ b/vendor/github.com/lestrrat-go/jwx/v3/jws/jwsbb/BUILD.bazel @@ -20,9 +20,7 @@ go_library( "//internal/base64", "//internal/ecutil", "//internal/keyconv", - "//internal/pool", "//internal/tokens", - "//jws/internal/keytype", "@com_github_lestrrat_go_dsig//:dsig", "@com_github_valyala_fastjson//:fastjson", ], @@ -30,10 +28,20 @@ go_library( go_test( name = "jwsbb_test", - srcs = ["jwsbb_test.go"], - embed = [":jwsbb"], + srcs = [ + "header_test.go", + "jwsbb_test.go", + ], deps = [ + ":jwsbb", "//internal/base64", + "//internal/pool", "@com_github_stretchr_testify//require", ], ) + +alias( + name = "go_default_library", + actual = ":jwsbb", + visibility = ["//visibility:public"], +) diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jws/jwsbb/sign.go b/vendor/github.com/lestrrat-go/jwx/v3/jws/jwsbb/sign.go index 8c0c185c54..bcc3bbbd31 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jws/jwsbb/sign.go +++ b/vendor/github.com/lestrrat-go/jwx/v3/jws/jwsbb/sign.go @@ -76,6 +76,11 @@ func dispatchRSASign(key any, dsigAlg string, payload []byte, rr io.Reader) ([]b return dsig.Sign(privkey, dsigAlg, payload, rr) } +// dispatchECDSASign does not enforce the RFC 7518 Section 3.4 binding +// between an ES* algorithm and its curve. That check lives one layer up, in +// jws, behind jws.WithStrictECDSA, because it is opt-in: signing a P-521 key +// under ES256 is non-conformant but has always been allowed here, and jwsbb +// is the raw building-block layer where the caller owns that decision. func dispatchECDSASign(key any, dsigAlg string, payload []byte, rr io.Reader) ([]byte, error) { // Try crypto.Signer first (dsig can handle it directly) if signer, ok := key.(crypto.Signer); ok { diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jws/key_provider.go b/vendor/github.com/lestrrat-go/jwx/v3/jws/key_provider.go index 49afd0e19f..a5475341a0 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jws/key_provider.go +++ b/vendor/github.com/lestrrat-go/jwx/v3/jws/key_provider.go @@ -10,6 +10,7 @@ import ( "github.com/lestrrat-go/jwx/v3/jwa" "github.com/lestrrat-go/jwx/v3/jwk" + "github.com/lestrrat-go/jwx/v3/jws/internal/keyalg" ) // KeyProvider is responsible for providing key(s) to sign or verify a payload. @@ -116,6 +117,10 @@ type keySetProvider struct { // It returns true if at least one pair was added, false if the key was // filtered out (e.g. wrong usage, no matching algorithm). func (kp *keySetProvider) selectKey(sink KeySink, key jwk.Key, sig *Signature, _ *Message) (bool, error) { + if uk, ok := key.(jwk.UnsupportedKey); ok { + return false, unsupportedKeyError(uk, `signature verification`) + } + if usage, ok := key.KeyUsage(); ok { // it's okay if use: "". we'll assume it's "sig" if usage != "" && usage != jwk.ForSignature.String() { @@ -138,7 +143,7 @@ func (kp *keySetProvider) selectKey(sink KeySink, key jwk.Key, sig *Signature, _ return false, nil } - algs, err := AlgorithmsForKey(key) + algs, err := keyalg.Candidates(key) if err != nil { return false, fmt.Errorf(`failed to get a list of signature methods for key type %s: %w`, key.KeyType(), err) } @@ -233,22 +238,23 @@ func (kp *keySetProvider) fetchKeysByKid(sink KeySink, sig *Signature, msg *Mess // fetchAllKeys iterates all keys in the set and adds suitable ones to the sink. // // When the protected header advertises an `alg`, keys whose type cannot -// produce that algorithm are skipped before reaching selectKey. This -// bounds verification fan-out to N_keys_of_matching_type instead of -// N_keys when `WithRequireKid(false)` is used against a heterogeneous -// JWKS. The skip is semantics-preserving: validateAlgorithmForKey in -// verify_context would reject the incompatible (alg, key) pair before -// running any verifier anyway. +// produce that algorithm are skipped before reaching selectKey +// (unsupported-key placeholders excepted — see the comment at the check +// below). This bounds verification fan-out to N_keys_of_matching_type +// instead of N_keys when `WithRequireKid(false)` is used against a +// heterogeneous JWKS. The skip is semantics-preserving: +// validateAlgorithmForKey in verify_context would reject the +// incompatible (alg, key) pair before running any verifier anyway. // // The allowed-KeyType set is looked up once per FetchKeys call via the -// precomputed algorithmToKeyTypes inverse map, so the per-key check is +// precomputed inverse map in keyalg, so the per-key check is // a cheap KeyType equality over a tiny slice (typically 1 element). // When allowedKtys is nil (no header alg, or alg has no registered // key type), the filter is skipped. func (kp *keySetProvider) fetchAllKeys(sink KeySink, sig *Signature, msg *Message) error { var allowedKtys []jwa.KeyType if hdrAlg, ok := sig.ProtectedHeaders().Algorithm(); ok { - allowedKtys = keyTypesForAlgorithm(hdrAlg) + allowedKtys = keyalg.KeyTypesFor(hdrAlg) } found := false var errs []error @@ -257,7 +263,13 @@ func (kp *keySetProvider) fetchAllKeys(sink KeySink, sig *Signature, msg *Messag if !ok { return fmt.Errorf(`failed to get key at index %d`, i) } - if allowedKtys != nil && !slices.Contains(allowedKtys, key.KeyType()) { + // Unsupported-key placeholders are exempt from the prefilter: + // their raw kty is never a registered KeyType, so the filter + // would silently skip them and the caller would only see a + // generic "no keys worked" error. Letting them reach selectKey + // records the per-key rejection (kid, kty, retained parse + // reason) in errs instead. + if allowedKtys != nil && !slices.Contains(allowedKtys, key.KeyType()) && !jwk.IsUnsupportedKey(key) { continue } added, err := kp.selectKey(sink, key, sig, msg) @@ -269,26 +281,16 @@ func (kp *keySetProvider) fetchAllKeys(sink KeySink, sig *Signature, msg *Messag found = true } } + // Only when no candidate reached the sink do the collected per-key + // errors become the outcome: a key that was skipped without error + // (e.g. no "alg" member and inference disabled) must not mask the + // named rejections of the keys that did fail. if !found && len(errs) > 0 { return fmt.Errorf(`no key in the key set was usable: %w`, errors.Join(errs...)) } return nil } -// keyTypesForAlgorithm returns the registered key types that can -// produce the given signature algorithm. The inverse map is maintained -// at registration time so this is an O(1) lookup. Returns nil if no -// key type is registered for alg, which signals callers to skip the -// prefilter. -func keyTypesForAlgorithm(alg jwa.SignatureAlgorithm) []jwa.KeyType { - muAlgorithmMaps.RLock() - defer muAlgorithmMaps.RUnlock() - // Copy so the caller can safely iterate without holding the - // lock; RegisterAlgorithmForKeyType may append concurrently - // after we return. Typical length is 1. - return slices.Clone(algorithmToKeyTypes[alg]) -} - type jkuProvider struct { fetcher jwk.Fetcher options []jwk.FetchOption @@ -329,13 +331,17 @@ func (kp jkuProvider) FetchKeys(ctx context.Context, sink KeySink, sig *Signatur return fmt.Errorf(`jku: key with "kid" %q not found in JWKS fetched from %q`, kid, u) } + if uk, ok := key.(jwk.UnsupportedKey); ok { + return fmt.Errorf(`jku: key with "kid" %q from %q has unsupported key type %q and cannot be used for signature verification; an extension module may be required to parse it: %w`, kid, u, uk.KeyType().String(), uk.Reason()) + } + if usage, ok := key.KeyUsage(); ok { if usage != "" && usage != jwk.ForSignature.String() { return fmt.Errorf(`key with kid %q is marked use=%q, not usable for signature verification (expected %q)`, kid, usage, jwk.ForSignature.String()) } } - algs, err := AlgorithmsForKey(key) + algs, err := keyalg.Candidates(key) if err != nil { return fmt.Errorf(`failed to get a list of signature methods for key type %s: %w`, key.KeyType(), err) } diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jws/legacy/BUILD.bazel b/vendor/github.com/lestrrat-go/jwx/v3/jws/legacy/BUILD.bazel index 8e77cece46..41ee14aa45 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jws/legacy/BUILD.bazel +++ b/vendor/github.com/lestrrat-go/jwx/v3/jws/legacy/BUILD.bazel @@ -14,8 +14,13 @@ go_library( deps = [ "//internal/ecutil", "//internal/keyconv", - "//internal/pool", "//jwa", "//jws/internal/keytype", ], ) + +alias( + name = "go_default_library", + actual = ":legacy", + visibility = ["//visibility:public"], +) diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jws/message.go b/vendor/github.com/lestrrat-go/jwx/v3/jws/message.go index 02e4590620..25814afd78 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jws/message.go +++ b/vendor/github.com/lestrrat-go/jwx/v3/jws/message.go @@ -9,6 +9,7 @@ import ( "github.com/lestrrat-go/jwx/v3/internal/pool" "github.com/lestrrat-go/jwx/v3/internal/tokens" "github.com/lestrrat-go/jwx/v3/jwa" + "github.com/lestrrat-go/jwx/v3/jwk" ) func NewSignature() *Signature { @@ -113,6 +114,15 @@ func (s *Signature) Sign(payload []byte, signer Signer, key any) ([]byte, []byte } func (s *Signature) sign2(payload []byte, signer interface{ Algorithm() jwa.SignatureAlgorithm }, key any) ([]byte, []byte, error) { + // A jwk.UnsupportedKey placeholder carries no usable key material and + // must never reach the Signer. This path builds a signatureBuilder + // directly and bypasses validateAlgorithmForKey (only the + // jws.Sign/WithKey path runs that guard), so reject the placeholder + // here — the single entry to signatureBuilder for this path. + if uk, ok := key.(jwk.UnsupportedKey); ok { + return nil, nil, unsupportedKeyError(uk, `signing`) + } + // Create a signatureBuilder to use the shared signing logic sb := signatureBuilderPool.Get() defer signatureBuilderPool.Put(sb) diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jws/options.go b/vendor/github.com/lestrrat-go/jwx/v3/jws/options.go index cc98abc5be..a126dd405c 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jws/options.go +++ b/vendor/github.com/lestrrat-go/jwx/v3/jws/options.go @@ -104,6 +104,14 @@ func (w *withKey) Protected(v Headers) Headers { // The algorithm specified in the `alg` parameter MUST be able to support // the type of key you provided, otherwise an error is returned. // +// RFC 7518 Section 3.4 binds each of ES256/ES384/ES512 to one elliptic curve +// (ES256/P-256, ES384/P-384, ES512/P-521), but `jws.Sign()` does not enforce +// that by default: a key on any other curve still signs, and the JWS it +// produces is one strict JOSE implementations reject. Pass +// `jws.WithStrictECDSA(true)` to `jws.Sign()` to reject the mismatch instead. +// `jws.Verify()` is unaffected either way and keeps inferring algorithms from +// a key's curve exactly as before. +// // Any of the following is accepted for the `key` parameter: // * A "raw" key (e.g. rsa.PrivateKey, ecdsa.PrivateKey, etc) // * A crypto.Signer diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jws/options.yaml b/vendor/github.com/lestrrat-go/jwx/v3/jws/options.yaml index fb8c88db62..326d14a745 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jws/options.yaml +++ b/vendor/github.com/lestrrat-go/jwx/v3/jws/options.yaml @@ -227,6 +227,28 @@ options: the key on-demand each time. By default, the key is not validated. + - ident: StrictECDSA + interface: SignOption + argument_type: bool + comment: | + WithStrictECDSA makes `jws.Sign()` reject anything RFC 7518 forbids for + an ECDSA signature. Today that is exactly one rule: Section 3.4 binds + ES256 to P-256, ES384 to P-384, and ES512 to P-521, so signing with a + key on any other curve fails instead of producing a JWS that strict + JOSE implementations reject. + + Future releases may enforce further RFC 7518 ECDSA rules under this + same option, so enabling it means "be strict about ECDSA", not "check + the curve and nothing else". + + Extension algorithms on their own curves, such as ES256K, are not + affected. Only the three curves the RFC names are checked. + + This option is sign-side only. `jws.Verify()` is unaffected and keeps + inferring algorithms from a key's curve exactly as before, so a JWS + produced without this option still verifies. + + By default, the curve is not checked. - ident: InferAlgorithmFromKey interface: WithKeySetSuboption argument_type: bool diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jws/options_gen.go b/vendor/github.com/lestrrat-go/jwx/v3/jws/options_gen.go index 10dd96e489..ade61d36c5 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jws/options_gen.go +++ b/vendor/github.com/lestrrat-go/jwx/v3/jws/options_gen.go @@ -223,6 +223,7 @@ type identProtectedHeaders struct{} type identPublicHeaders struct{} type identRequireKid struct{} type identSerialization struct{} +type identStrictECDSA struct{} type identUseDefault struct{} type identValidateKey struct{} @@ -306,6 +307,10 @@ func (identSerialization) String() string { return "WithSerialization" } +func (identStrictECDSA) String() string { + return "WithStrictECDSA" +} + func (identUseDefault) String() string { return "WithUseDefault" } @@ -605,6 +610,28 @@ func WithCompact() SignVerifyParseOption { return &signVerifyParseOption{option.New(identSerialization{}, fmtCompact)} } +// WithStrictECDSA makes `jws.Sign()` reject anything RFC 7518 forbids for +// an ECDSA signature. Today that is exactly one rule: Section 3.4 binds +// ES256 to P-256, ES384 to P-384, and ES512 to P-521, so signing with a +// key on any other curve fails instead of producing a JWS that strict +// JOSE implementations reject. +// +// Future releases may enforce further RFC 7518 ECDSA rules under this +// same option, so enabling it means "be strict about ECDSA", not "check +// the curve and nothing else". +// +// Extension algorithms on their own curves, such as ES256K, are not +// affected. Only the three curves the RFC names are checked. +// +// This option is sign-side only. `jws.Verify()` is unaffected and keeps +// inferring algorithms from a key's curve exactly as before, so a JWS +// produced without this option still verifies. +// +// By default, the curve is not checked. +func WithStrictECDSA(v bool) SignOption { + return &signOption{option.New(identStrictECDSA{}, v)} +} + // WithUseDefault specifies that if and only if a jwk.Key contains // exactly one jwk.Key, that key should be used. func WithUseDefault(v bool) WithKeySetSuboption { diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jws/sign_context.go b/vendor/github.com/lestrrat-go/jwx/v3/jws/sign_context.go index a6cbd045e9..f564454f18 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jws/sign_context.go +++ b/vendor/github.com/lestrrat-go/jwx/v3/jws/sign_context.go @@ -14,6 +14,7 @@ type signContext struct { format int detached bool validateKey bool + strictECDSA bool payload []byte payloadReader io.Reader encoder Base64Encoder @@ -39,6 +40,7 @@ func freeSignContext(ctx *signContext) *signContext { ctx.sigbuilders = ctx.sigbuilders[:0] ctx.detached = false ctx.validateKey = false + ctx.strictECDSA = false ctx.encoder = base64.DefaultEncoder() ctx.none = nil ctx.payload = nil @@ -133,6 +135,10 @@ func (sc *signContext) ProcessOptions(options []SignOption) error { if err := option.Value(&sc.validateKey); err != nil { return makeSignError(prefixJwsSign, `failed to retrieve validate-key option value: %w`, err) } + case identStrictECDSA{}: + if err := option.Value(&sc.strictECDSA); err != nil { + return makeSignError(prefixJwsSign, `failed to retrieve strict-ECDSA option value: %w`, err) + } case identBase64Encoder{}: if err := option.Value(&sc.encoder); err != nil { return makeSignError(prefixJwsSign, `failed to retrieve base64-encoder option value: %w`, err) diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jws/signature_builder.go b/vendor/github.com/lestrrat-go/jwx/v3/jws/signature_builder.go index 2963b6e48d..76ed62521b 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jws/signature_builder.go +++ b/vendor/github.com/lestrrat-go/jwx/v3/jws/signature_builder.go @@ -2,14 +2,19 @@ package jws import ( "bytes" + "crypto/ecdsa" "fmt" "slices" + "github.com/lestrrat-go/dsig" + "github.com/lestrrat-go/jwx/v3/internal/json" + "github.com/lestrrat-go/jwx/v3/internal/keyconv" "github.com/lestrrat-go/jwx/v3/internal/pool" "github.com/lestrrat-go/jwx/v3/internal/tokens" "github.com/lestrrat-go/jwx/v3/jwa" "github.com/lestrrat-go/jwx/v3/jwk" + jwsbbi "github.com/lestrrat-go/jwx/v3/jws/internal/jwsbb" "github.com/lestrrat-go/jwx/v3/jws/jwsbb" ) @@ -52,7 +57,60 @@ func freeSignatureBuilder(sb *signatureBuilder) *signatureBuilder { return sb } +// requireECDSACurve enforces the RFC 7518 Section 3.4 binding between an ES* +// algorithm and the curve its key must sit on. It is only reached when the +// caller asked for it with jws.WithStrictECDSA(true). +// +// Anything that is not an ECDSA signature passes straight through, as does an +// ECDSA-family algorithm outside the three JOSE built-ins (an extension on its +// own curve, such as ES256K) and a key whose curve cannot be read. Deciding +// those cases is not this check's job; only positive evidence of a mismatch is +// an error. +func requireECDSACurve(alg jwa.SignatureAlgorithm, key any) error { + dsigAlg, ok := jwsbb.GetDsigAlgorithm(alg.String()) + if !ok { + return nil + } + + info, ok := dsig.GetAlgorithmInfo(dsigAlg) + if !ok || info.Family != dsig.ECDSA { + return nil + } + + rawKey, ok := unwrapECDSASignKey(key) + if !ok { + return nil + } + + return jwsbbi.RequireECDSACurve(alg.String(), dsigAlg, rawKey) +} + +// unwrapECDSASignKey returns the key jwsbbi.RequireECDSACurve should inspect. +// That function reads the curve off a raw key or a crypto.Signer, so a +// jwk.Key has to be unwrapped first. +// +// The bool is false when key is a jwk.Key holding something other than an +// ECDSA private key, which leaves the curve unreadable. The caller skips the +// check in that case and lets the signer reject the key on its own terms. +func unwrapECDSASignKey(key any) (any, bool) { + if _, ok := key.(jwk.Key); !ok { + return key, true + } + + var privkey *ecdsa.PrivateKey + if err := keyconv.ECDSAPrivateKey(&privkey, key); err != nil { + return nil, false + } + return privkey, true +} + func (sb *signatureBuilder) Build(sc *signContext, payload []byte) (*Signature, error) { + if sc.strictECDSA { + if err := requireECDSACurve(sb.alg, sb.key); err != nil { + return nil, makeSignError(prefixJwsSign, `%w`, err) + } + } + // Clone caller-provided headers before mutating so that re-using the // same Headers instance across multiple Sign calls does not cause // cross-contamination of alg/kid. diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jws/streaming_detached.go b/vendor/github.com/lestrrat-go/jwx/v3/jws/streaming_detached.go index 871bb6439d..5a23038350 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jws/streaming_detached.go +++ b/vendor/github.com/lestrrat-go/jwx/v3/jws/streaming_detached.go @@ -16,6 +16,7 @@ import ( "github.com/lestrrat-go/jwx/v3/internal/tokens" "github.com/lestrrat-go/jwx/v3/jwa" "github.com/lestrrat-go/jwx/v3/jwk" + jwsbbi "github.com/lestrrat-go/jwx/v3/jws/internal/jwsbb" "github.com/lestrrat-go/jwx/v3/jws/jwsbb" ) @@ -85,6 +86,14 @@ func (sc *signContext) signStreaming() ([]byte, error) { return nil, makeSignError(prefixJwsSign, `failed to convert key for signature %d: %w`, idx, err) } + // The non-streaming path runs the same check from + // signatureBuilder.Build, which this path does not go through. + if sc.strictECDSA && dsigInfo.Family == dsig.ECDSA { + if err := jwsbbi.RequireECDSACurve(alg.String(), dsigInfo.Name, rawKey); err != nil { + return nil, makeSignError(prefixJwsSign, `signature %d: %w`, idx, err) + } + } + protected, err := cloneOrNewHeaders(sb.protected) if err != nil { return nil, makeSignError(prefixJwsSign, `failed to clone protected headers for signature %d: %w`, idx, err) diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jws/verify_context.go b/vendor/github.com/lestrrat-go/jwx/v3/jws/verify_context.go index f9c2421f34..f4aed3b368 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jws/verify_context.go +++ b/vendor/github.com/lestrrat-go/jwx/v3/jws/verify_context.go @@ -13,6 +13,7 @@ import ( "github.com/lestrrat-go/jwx/v3/internal/json" "github.com/lestrrat-go/jwx/v3/internal/pool" "github.com/lestrrat-go/jwx/v3/jwa" + "github.com/lestrrat-go/jwx/v3/jwk" "github.com/lestrrat-go/jwx/v3/jws/jwsbb" ) @@ -287,6 +288,15 @@ func (vc *verifyContext) VerifyMessage(buf []byte) ([]byte, error) { } func (vc *verifyContext) tryKey(verifyBuf []byte, alg jwa.SignatureAlgorithm, key any, msg *Message, sig *Signature) error { + // Reject placeholders before any verifier — including a custom + // Verifier2 — can see them. A custom KeyProvider can sink an + // (alg, key) pair directly, bypassing keySetProvider.selectKey and + // validateAlgorithmForKey, so this is the last chokepoint before + // key material is used. + if uk, ok := key.(jwk.UnsupportedKey); ok { + return unsupportedKeyError(uk, `signature verification`) + } + if vc.validateKey { if err := validateKeyBeforeUse(key); err != nil { return fmt.Errorf(`failed to validate key before verification: %w`, err) diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwt/BUILD.bazel b/vendor/github.com/lestrrat-go/jwx/v3/jwt/BUILD.bazel index 86197d348a..d49f6c1b48 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jwt/BUILD.bazel +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwt/BUILD.bazel @@ -4,9 +4,10 @@ go_library( name = "jwt", srcs = [ "builder_gen.go", + "doc.go", "errors.go", - "filter.go", "fastpath.go", + "filter.go", "http.go", "interface.go", "io.go", @@ -24,17 +25,17 @@ go_library( deps = [ "//:jwx", "//internal/base64", - "//transform", "//internal/json", - "//internal/tokens", "//internal/pool", + "//internal/tokens", "//jwa", "//jwe", "//jwk", "//jws", "//jws/jwsbb", - "//jwt/internal/types", "//jwt/internal/errors", + "//jwt/internal/types", + "//transform", "@com_github_lestrrat_go_blackmagic//:blackmagic", "@com_github_lestrrat_go_option_v2//:option", ], @@ -43,6 +44,10 @@ go_library( go_test( name = "jwt_test", srcs = [ + "fastpath_test.go", + "filter_test.go", + "fuzz_test.go", + "jwt_crit_test.go", "jwt_test.go", "options_gen_test.go", "token_options_test.go", @@ -54,13 +59,14 @@ go_test( deps = [ "//internal/json", "//internal/jwxtest", + "//internal/tokens", "//jwa", "//jwe", "//jwk", "//jwk/ecdsa", "//jws", "//jwt/internal/types", - "@com_github_lestrrat_go_httprc_v3//:httprc", + "@com_github_lestrrat_go_httprc_v3//:httprc", "@com_github_stretchr_testify//require", ], ) diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwt/internal/errors/BUILD.bazel b/vendor/github.com/lestrrat-go/jwx/v3/jwt/internal/errors/BUILD.bazel index a053e8c0aa..cb5c4099c5 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jwt/internal/errors/BUILD.bazel +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwt/internal/errors/BUILD.bazel @@ -2,9 +2,7 @@ load("@rules_go//go:def.bzl", "go_library") go_library( name = "errors", - srcs = [ - "errors.go", - ], + srcs = ["errors.go"], importpath = "github.com/lestrrat-go/jwx/v3/jwt/internal/errors", visibility = ["//jwt:__subpackages__"], ) @@ -13,4 +11,4 @@ alias( name = "go_default_library", actual = ":errors", visibility = ["//jwt:__subpackages__"], -) \ No newline at end of file +) diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwt/jwt.go b/vendor/github.com/lestrrat-go/jwx/v3/jwt/jwt.go index 6229c763cd..7bbd2686bf 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jwt/jwt.go +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwt/jwt.go @@ -517,7 +517,7 @@ OUTER: // the token. // // For well-known algorithms with no special considerations (e.g. detached -// payloads, extra protected heders, etc), this function will automatically +// payloads, extra protected headers, etc), this function will automatically // take the fast path and bypass the jws.Sign() machinery, which improves // performance significantly. // diff --git a/vendor/github.com/lestrrat-go/jwx/v3/jwt/token_gen.go b/vendor/github.com/lestrrat-go/jwx/v3/jwt/token_gen.go index 7a057742a9..343e399473 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/jwt/token_gen.go +++ b/vendor/github.com/lestrrat-go/jwx/v3/jwt/token_gen.go @@ -628,9 +628,9 @@ func (t *stdToken) MarshalJSON() ([]byte, error) { if i > 0 { buf.WriteByte(tokens.Comma) } - buf.WriteByte('"') - buf.WriteString(pair.Name) - buf.WriteString(`": `) + if err := json.WriteQuotedKey(buf, pair.Name); err != nil { + return nil, fmt.Errorf(`failed to encode claim name %q: %w`, pair.Name, err) + } buf.Write(pair.Value.([]byte)) } buf.WriteByte(tokens.CloseCurlyBracket) diff --git a/vendor/github.com/lestrrat-go/jwx/v3/transform/BUILD.bazel b/vendor/github.com/lestrrat-go/jwx/v3/transform/BUILD.bazel index 3333c6607c..9abb3aa5df 100644 --- a/vendor/github.com/lestrrat-go/jwx/v3/transform/BUILD.bazel +++ b/vendor/github.com/lestrrat-go/jwx/v3/transform/BUILD.bazel @@ -15,9 +15,7 @@ go_library( go_test( name = "transform_test", - srcs = [ - "map_test.go", - ], + srcs = ["map_test.go"], deps = [ ":transform", "//jwt", @@ -29,4 +27,4 @@ alias( name = "go_default_library", actual = ":transform", visibility = ["//visibility:public"], -) \ No newline at end of file +) diff --git a/vendor/github.com/open-policy-agent/opa/capabilities/capabilities.go b/vendor/github.com/open-policy-agent/opa/capabilities/capabilities.go index 5482720f7c..c2c35f744e 100644 --- a/vendor/github.com/open-policy-agent/opa/capabilities/capabilities.go +++ b/vendor/github.com/open-policy-agent/opa/capabilities/capabilities.go @@ -1,7 +1,10 @@ -// Copyright 2021 The OPA Authors. All rights reserved. +// Copyright 2026 The OPA Authors. All rights reserved. // Use of this source code is governed by an Apache2 // license that can be found in the LICENSE file. +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. package capabilities import ( diff --git a/vendor/github.com/open-policy-agent/opa/capabilities/doc.go b/vendor/github.com/open-policy-agent/opa/capabilities/doc.go deleted file mode 100644 index 189c2e727a..0000000000 --- a/vendor/github.com/open-policy-agent/opa/capabilities/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2024 The OPA Authors. All rights reserved. -// Use of this source code is governed by an Apache2 -// license that can be found in the LICENSE file. - -// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. -// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. -// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. -package capabilities diff --git a/vendor/github.com/open-policy-agent/opa/capabilities/v1.20.2.json b/vendor/github.com/open-policy-agent/opa/capabilities/v1.20.2.json new file mode 100644 index 0000000000..ccecfa24b1 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/capabilities/v1.20.2.json @@ -0,0 +1,5028 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "and", + "decl": { + "args": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + }, + "infix": "\u0026" + }, + { + "name": "any", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "array.concat", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "array.flatten", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "array.reverse", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "array.slice", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "assign", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": ":=" + }, + { + "name": "base64.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64.encode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "base64url.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode_no_pad", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "bits.and", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.lsh", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.negate", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.or", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.rsh", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.xor", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "cast_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "null" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "ceil", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "contains", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "count", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.equal", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.md5", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.sha1", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.sha256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.sha512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.parse_private_keys", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "crypto.sha1", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.sha256", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_and_verify_certificates", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_and_verify_certificates_with_options", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_certificate_request", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_certificates", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_keypair", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_rsa_private_key", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "div", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "infix": "/" + }, + { + "name": "endswith", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "eq", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "=" + }, + { + "name": "equal", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "==" + }, + { + "name": "floor", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "type": "null" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "type": "any" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "glob.quote_meta", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "graph.reachable", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "graph.reachable_paths", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "graphql.is_valid", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "graphql.parse", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "graphql.parse_and_verify", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "graphql.parse_query", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "graphql.parse_schema", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "graphql.schema_is_valid", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "gt", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "\u003e" + }, + { + "name": "gte", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "\u003e=" + }, + { + "name": "hex.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "hex.encode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "http.send", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "indexof_n", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "internal.member_2", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "in" + }, + { + "name": "internal.member_3", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "in" + }, + { + "name": "internal.print", + "decl": { + "args": [ + { + "dynamic": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "array" + } + ], + "type": "function" + } + }, + { + "name": "internal.template_string", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "internal.test_case", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "function" + } + }, + { + "name": "intersection", + "decl": { + "args": [ + { + "of": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "static": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode_verify", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "array" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.verify_eddsa", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "json.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "json.marshal_with_options", + "decl": { + "args": [ + { + "type": "any" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "static": [ + { + "key": "indent", + "value": { + "type": "string" + } + }, + { + "key": "prefix", + "value": { + "type": "string" + } + }, + { + "key": "pretty", + "value": { + "type": "boolean" + } + } + ], + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "json.match_schema", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "static": [ + { + "key": "desc", + "value": { + "type": "string" + } + }, + { + "key": "error", + "value": { + "type": "string" + } + }, + { + "key": "field", + "value": { + "type": "string" + } + }, + { + "key": "type", + "value": { + "type": "string" + } + } + ], + "type": "object" + }, + "type": "array" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "json.patch", + "decl": { + "args": [ + { + "type": "any" + }, + { + "dynamic": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "static": [ + { + "key": "op", + "value": { + "type": "string" + } + }, + { + "key": "path", + "value": { + "type": "any" + } + } + ], + "type": "object" + }, + "type": "array" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "json.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "json.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "json.verify_schema", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "of": [ + { + "type": "null" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "lower", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "lt", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "\u003c" + }, + { + "name": "lte", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "\u003c=" + }, + { + "name": "max", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "minus", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "number" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": [ + { + "type": "number" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + }, + "type": "function" + }, + "infix": "-" + }, + { + "name": "mul", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "infix": "*" + }, + { + "name": "neq", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "!=" + }, + { + "name": "net.cidr_contains", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "net.cidr_contains_matches", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "static": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "net.cidr_expand", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "of": { + "type": "string" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "net.cidr_intersects", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "net.cidr_is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "net.cidr_merge", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "string" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "net.cidr_overlap", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "net.lookup_ip_addr", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "of": { + "type": "string" + }, + "type": "set" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "numbers.range", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "numbers.range_step", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "object.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "object.get", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "object.keys", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "object.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "object.subset", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "object.union", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "object.union_n", + "decl": { + "args": [ + { + "dynamic": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "or", + "decl": { + "args": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + }, + "infix": "|" + }, + { + "name": "plus", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "infix": "+" + }, + { + "name": "print", + "decl": { + "type": "function", + "variadic": { + "type": "any" + } + } + }, + { + "name": "product", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + { + "of": { + "type": "number" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "providers.aws.sign_req", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "rand.intn", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "re_match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "regex.find_all_string_submatch_n", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "regex.find_n", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "regex.globs_match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "regex.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "regex.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "regex.replace", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "regex.split", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "regex.template_match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "rego.metadata.chain", + "decl": { + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "rego.metadata.rule", + "decl": { + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "rego.parse_module", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "rem", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "infix": "%" + }, + { + "name": "replace", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "round", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "semver.compare", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "semver.is_valid", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "set_diff", + "decl": { + "args": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "sort", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "split", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "sprintf", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "startswith", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "strings.any_prefix_match", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "strings.any_suffix_match", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "strings.count", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "strings.render_template", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.reverse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.split_n", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "substring", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "sum", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + { + "of": { + "type": "number" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.add_date", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.clock", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "time.date", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "time.diff", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "time.format", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "time.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "time.parse_duration_ns", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.parse_ns", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.parse_rfc3339_ns", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.weekday", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "to_number", + "decl": { + "args": [ + { + "of": [ + { + "type": "null" + }, + { + "type": "boolean" + }, + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "trace", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "trim", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_left", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_prefix", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_right", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_space", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_suffix", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "type_name", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "union", + "decl": { + "args": [ + { + "of": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "units.parse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "units.parse_bytes", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "upper", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "uri.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "uri.parse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "urlquery.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "urlquery.decode_object", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "dynamic": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "urlquery.encode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "urlquery.encode_object", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "uuid.parse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "uuid.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "walk", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + }, + "relation": true + }, + { + "name": "yaml.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "yaml.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "yaml.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + } + ], + "future_keywords": [ + "and", + "not", + "or" + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ], + "features": [ + "keywords_in_refs", + "rego_v1", + "template_strings" + ] +} diff --git a/vendor/github.com/open-policy-agent/opa/capabilities/v1.21.0.json b/vendor/github.com/open-policy-agent/opa/capabilities/v1.21.0.json new file mode 100644 index 0000000000..73f2fe4b6d --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/capabilities/v1.21.0.json @@ -0,0 +1,5030 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "and", + "decl": { + "args": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + }, + "infix": "\u0026" + }, + { + "name": "any", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "array.concat", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "array.flatten", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "array.reverse", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "array.slice", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "assign", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": ":=" + }, + { + "name": "base64.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64.encode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "base64url.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode_no_pad", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "bits.and", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.lsh", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.negate", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.or", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.rsh", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.xor", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "cast_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "null" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "ceil", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "contains", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "count", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.equal", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.md5", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.sha1", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.sha256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.sha512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.parse_private_keys", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "crypto.sha1", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.sha256", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_and_verify_certificates", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_and_verify_certificates_with_options", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_certificate_request", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_certificates", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_keypair", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_rsa_private_key", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "div", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "infix": "/" + }, + { + "name": "endswith", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "eq", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "=" + }, + { + "name": "equal", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "==" + }, + { + "name": "floor", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "type": "null" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "type": "any" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "glob.quote_meta", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "graph.reachable", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "graph.reachable_paths", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "graphql.is_valid", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "graphql.parse", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "graphql.parse_and_verify", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "graphql.parse_query", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "graphql.parse_schema", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "graphql.schema_is_valid", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "gt", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "\u003e" + }, + { + "name": "gte", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "\u003e=" + }, + { + "name": "hex.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "hex.encode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "http.send", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "indexof_n", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "internal.member_2", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "in" + }, + { + "name": "internal.member_3", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "in" + }, + { + "name": "internal.print", + "decl": { + "args": [ + { + "dynamic": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "array" + } + ], + "type": "function" + } + }, + { + "name": "internal.template_string", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "internal.test_case", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "function" + } + }, + { + "name": "intersection", + "decl": { + "args": [ + { + "of": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "static": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode_verify", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "array" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.verify_eddsa", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "json.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "json.marshal_with_options", + "decl": { + "args": [ + { + "type": "any" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "static": [ + { + "key": "indent", + "value": { + "type": "string" + } + }, + { + "key": "prefix", + "value": { + "type": "string" + } + }, + { + "key": "pretty", + "value": { + "type": "boolean" + } + } + ], + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "json.match_schema", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "static": [ + { + "key": "desc", + "value": { + "type": "string" + } + }, + { + "key": "error", + "value": { + "type": "string" + } + }, + { + "key": "field", + "value": { + "type": "string" + } + }, + { + "key": "type", + "value": { + "type": "string" + } + } + ], + "type": "object" + }, + "type": "array" + } + ], + "type": "array" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "json.patch", + "decl": { + "args": [ + { + "type": "any" + }, + { + "dynamic": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "static": [ + { + "key": "op", + "value": { + "type": "string" + } + }, + { + "key": "path", + "value": { + "type": "any" + } + } + ], + "type": "object" + }, + "type": "array" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "json.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "json.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "json.verify_schema", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "of": [ + { + "type": "null" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "lower", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "lt", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "\u003c" + }, + { + "name": "lte", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "\u003c=" + }, + { + "name": "max", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "minus", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "number" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": [ + { + "type": "number" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + }, + "type": "function" + }, + "infix": "-" + }, + { + "name": "mul", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "infix": "*" + }, + { + "name": "neq", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "!=" + }, + { + "name": "net.cidr_contains", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "net.cidr_contains_matches", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "static": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "net.cidr_expand", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "of": { + "type": "string" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "net.cidr_intersects", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "net.cidr_is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "net.cidr_merge", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "string" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "net.cidr_overlap", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "net.lookup_ip_addr", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "of": { + "type": "string" + }, + "type": "set" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "numbers.range", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "numbers.range_step", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "object.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "object.get", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "object.keys", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "object.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "object.subset", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "object.union", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "object.union_n", + "decl": { + "args": [ + { + "dynamic": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "or", + "decl": { + "args": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + }, + "infix": "|" + }, + { + "name": "plus", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "infix": "+" + }, + { + "name": "print", + "decl": { + "type": "function", + "variadic": { + "type": "any" + } + } + }, + { + "name": "product", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + { + "of": { + "type": "number" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "providers.aws.sign_req", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "rand.intn", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "re_match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "regex.find_all_string_submatch_n", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "regex.find_n", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "regex.globs_match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "regex.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "regex.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "regex.replace", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "regex.split", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "regex.template_match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "rego.metadata.chain", + "decl": { + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "rego.metadata.rule", + "decl": { + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "rego.parse_module", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "rem", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "infix": "%" + }, + { + "name": "replace", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "round", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "semver.compare", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "semver.is_valid", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "set_diff", + "decl": { + "args": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "sort", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "split", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "sprintf", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "startswith", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "strings.any_prefix_match", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "strings.any_suffix_match", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "strings.count", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "strings.render_template", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.reverse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.split_n", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "substring", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "sum", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + { + "of": { + "type": "number" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.add_date", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.clock", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "time.date", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "time.diff", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "time.format", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "time.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "time.parse_duration_ns", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.parse_ns", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.parse_rfc3339_ns", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.weekday", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "to_number", + "decl": { + "args": [ + { + "of": [ + { + "type": "null" + }, + { + "type": "boolean" + }, + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "trace", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "trim", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_left", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_prefix", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_right", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_space", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_suffix", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "type_name", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "union", + "decl": { + "args": [ + { + "of": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "units.parse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "units.parse_bytes", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "upper", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "uri.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "uri.parse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "urlquery.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "urlquery.decode_object", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "dynamic": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "urlquery.encode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "urlquery.encode_object", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "uuid.parse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "uuid.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "walk", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + }, + "relation": true + }, + { + "name": "yaml.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "yaml.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "yaml.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + } + ], + "future_keywords": [ + "and", + "not", + "or" + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ], + "features": [ + "keywords_in_refs", + "rego_v1", + "template_strings" + ] +} diff --git a/vendor/github.com/open-policy-agent/opa/capabilities/v1.21.1.json b/vendor/github.com/open-policy-agent/opa/capabilities/v1.21.1.json new file mode 100644 index 0000000000..73f2fe4b6d --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/capabilities/v1.21.1.json @@ -0,0 +1,5030 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "and", + "decl": { + "args": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + }, + "infix": "\u0026" + }, + { + "name": "any", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "array.concat", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "array.flatten", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "array.reverse", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "array.slice", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "assign", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": ":=" + }, + { + "name": "base64.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64.encode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "base64url.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode_no_pad", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "bits.and", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.lsh", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.negate", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.or", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.rsh", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.xor", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "cast_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "null" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "ceil", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "contains", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "count", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.equal", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.md5", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.sha1", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.sha256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.sha512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.parse_private_keys", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "crypto.sha1", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.sha256", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_and_verify_certificates", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_and_verify_certificates_with_options", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_certificate_request", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_certificates", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_keypair", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_rsa_private_key", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "div", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "infix": "/" + }, + { + "name": "endswith", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "eq", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "=" + }, + { + "name": "equal", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "==" + }, + { + "name": "floor", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "type": "null" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "type": "any" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "glob.quote_meta", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "graph.reachable", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "graph.reachable_paths", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "graphql.is_valid", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "graphql.parse", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "graphql.parse_and_verify", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "graphql.parse_query", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "graphql.parse_schema", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "graphql.schema_is_valid", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "gt", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "\u003e" + }, + { + "name": "gte", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "\u003e=" + }, + { + "name": "hex.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "hex.encode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "http.send", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "indexof_n", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "internal.member_2", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "in" + }, + { + "name": "internal.member_3", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "in" + }, + { + "name": "internal.print", + "decl": { + "args": [ + { + "dynamic": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "array" + } + ], + "type": "function" + } + }, + { + "name": "internal.template_string", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "internal.test_case", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "function" + } + }, + { + "name": "intersection", + "decl": { + "args": [ + { + "of": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "static": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode_verify", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "array" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.verify_eddsa", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "json.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "json.marshal_with_options", + "decl": { + "args": [ + { + "type": "any" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "static": [ + { + "key": "indent", + "value": { + "type": "string" + } + }, + { + "key": "prefix", + "value": { + "type": "string" + } + }, + { + "key": "pretty", + "value": { + "type": "boolean" + } + } + ], + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "json.match_schema", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "static": [ + { + "key": "desc", + "value": { + "type": "string" + } + }, + { + "key": "error", + "value": { + "type": "string" + } + }, + { + "key": "field", + "value": { + "type": "string" + } + }, + { + "key": "type", + "value": { + "type": "string" + } + } + ], + "type": "object" + }, + "type": "array" + } + ], + "type": "array" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "json.patch", + "decl": { + "args": [ + { + "type": "any" + }, + { + "dynamic": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "static": [ + { + "key": "op", + "value": { + "type": "string" + } + }, + { + "key": "path", + "value": { + "type": "any" + } + } + ], + "type": "object" + }, + "type": "array" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "json.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "json.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "json.verify_schema", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "of": [ + { + "type": "null" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "lower", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "lt", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "\u003c" + }, + { + "name": "lte", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "\u003c=" + }, + { + "name": "max", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "minus", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "number" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": [ + { + "type": "number" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + }, + "type": "function" + }, + "infix": "-" + }, + { + "name": "mul", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "infix": "*" + }, + { + "name": "neq", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "!=" + }, + { + "name": "net.cidr_contains", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "net.cidr_contains_matches", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "static": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "net.cidr_expand", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "of": { + "type": "string" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "net.cidr_intersects", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "net.cidr_is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "net.cidr_merge", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "string" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "net.cidr_overlap", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "net.lookup_ip_addr", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "of": { + "type": "string" + }, + "type": "set" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "numbers.range", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "numbers.range_step", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "object.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "object.get", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "object.keys", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "object.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "object.subset", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "object.union", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "object.union_n", + "decl": { + "args": [ + { + "dynamic": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "or", + "decl": { + "args": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + }, + "infix": "|" + }, + { + "name": "plus", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "infix": "+" + }, + { + "name": "print", + "decl": { + "type": "function", + "variadic": { + "type": "any" + } + } + }, + { + "name": "product", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + { + "of": { + "type": "number" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "providers.aws.sign_req", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "rand.intn", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "re_match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "regex.find_all_string_submatch_n", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "regex.find_n", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "regex.globs_match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "regex.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "regex.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "regex.replace", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "regex.split", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "regex.template_match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "rego.metadata.chain", + "decl": { + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "rego.metadata.rule", + "decl": { + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "rego.parse_module", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "rem", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "infix": "%" + }, + { + "name": "replace", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "round", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "semver.compare", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "semver.is_valid", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "set_diff", + "decl": { + "args": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "sort", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "split", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "sprintf", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "startswith", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "strings.any_prefix_match", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "strings.any_suffix_match", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "strings.count", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "strings.render_template", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.reverse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.split_n", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "substring", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "sum", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + { + "of": { + "type": "number" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.add_date", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.clock", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "time.date", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "time.diff", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "time.format", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "time.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "time.parse_duration_ns", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.parse_ns", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.parse_rfc3339_ns", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.weekday", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "to_number", + "decl": { + "args": [ + { + "of": [ + { + "type": "null" + }, + { + "type": "boolean" + }, + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "trace", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "trim", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_left", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_prefix", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_right", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_space", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_suffix", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "type_name", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "union", + "decl": { + "args": [ + { + "of": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "units.parse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "units.parse_bytes", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "upper", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "uri.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "uri.parse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "urlquery.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "urlquery.decode_object", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "dynamic": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "urlquery.encode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "urlquery.encode_object", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "uuid.parse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "uuid.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "walk", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + }, + "relation": true + }, + { + "name": "yaml.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "yaml.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "yaml.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + } + ], + "future_keywords": [ + "and", + "not", + "or" + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ], + "features": [ + "keywords_in_refs", + "rego_v1", + "template_strings" + ] +} diff --git a/vendor/github.com/open-policy-agent/opa/internal/cidr/merge/merge.go b/vendor/github.com/open-policy-agent/opa/internal/cidr/merge/merge.go index 85695b1292..e92c8d485c 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/cidr/merge/merge.go +++ b/vendor/github.com/open-policy-agent/opa/internal/cidr/merge/merge.go @@ -22,6 +22,7 @@ import ( "encoding/binary" "math/big" "net" + "slices" ) const ( @@ -61,7 +62,7 @@ func RangeToCIDRs(firstIP, lastIP net.IP) []*net.IPNet { } else { bitLen = ipv6BitLen } - _, _, right := partitionCIDR(spanningCIDR, net.IPNet{IP: prevFirstRangeIP, Mask: net.CIDRMask(bitLen, bitLen)}) + _, right := partitionCIDR(spanningCIDR, net.IPNet{IP: prevFirstRangeIP, Mask: net.CIDRMask(bitLen, bitLen)}) // Append all CIDRs but the first, as this CIDR includes the upper // bound of the spanning CIDR, which we still need to partition on. @@ -84,7 +85,7 @@ func RangeToCIDRs(firstIP, lastIP net.IP) []*net.IPNet { } else { bitLen = ipv6BitLen } - left, _, _ := partitionCIDR(spanningCIDR, net.IPNet{IP: nextFirstRangeIP, Mask: net.CIDRMask(bitLen, bitLen)}) + left, _ := partitionCIDR(spanningCIDR, net.IPNet{IP: nextFirstRangeIP, Mask: net.CIDRMask(bitLen, bitLen)}) cidrList = append(cidrList, left...) } else { // Otherwise, there is no need to partition; just use add the spanning @@ -110,8 +111,7 @@ func GetAddressRange(ipNet net.IPNet) (net.IP, net.IP) { lastIP = append(v4Mappedv6Prefix, lastIP...) } - lastIPMask := make(net.IPMask, len(ipNet.Mask)) - copy(lastIPMask, ipNet.Mask) + lastIPMask := slices.Clone(ipNet.Mask) for i := range lastIPMask { lastIPMask[len(lastIPMask)-i-1] = ^lastIPMask[len(lastIPMask)-i-1] lastIP[net.IPv6len-i-1] |= lastIPMask[len(lastIPMask)-i-1] @@ -127,8 +127,7 @@ func GetPreviousIP(ip net.IP) net.IP { return ip } - previousIP := make(net.IP, len(ip)) - copy(previousIP, ip) + previousIP := slices.Clone(ip) var overflow bool var lowerByteBound int @@ -215,7 +214,7 @@ func createSpanningCIDR(firstIP, lastIP *net.IP) net.IPNet { // contained within the targetCIDR (nil otherwise), and the // third is a list containing the networks to the right of the excludeCIDR in // the partition. -func partitionCIDR(targetCIDR net.IPNet, excludeCIDR net.IPNet) ([]*net.IPNet, []*net.IPNet, []*net.IPNet) { +func partitionCIDR(targetCIDR net.IPNet, excludeCIDR net.IPNet) ([]*net.IPNet, []*net.IPNet) { var targetIsIPv4 bool if targetCIDR.IP.To4() != nil { targetIsIPv4 = true @@ -228,25 +227,20 @@ func partitionCIDR(targetCIDR net.IPNet, excludeCIDR net.IPNet) ([]*net.IPNet, [ excludeMaskSize, _ := excludeCIDR.Mask.Size() if bytes.Compare(excludeLastIP, targetFirstIP) < 0 { - return nil, nil, []*net.IPNet{&targetCIDR} + return nil, []*net.IPNet{&targetCIDR} } else if bytes.Compare(targetLastIP, excludeFirstIP) < 0 { - return []*net.IPNet{&targetCIDR}, nil, nil + return []*net.IPNet{&targetCIDR}, nil } if targetMaskSize >= excludeMaskSize { - return nil, []*net.IPNet{&targetCIDR}, nil + return nil, nil } left := []*net.IPNet{} right := []*net.IPNet{} newPrefixLen := targetMaskSize + 1 - - targetFirstCopy := make(net.IP, len(targetFirstIP)) - copy(targetFirstCopy, targetFirstIP) - - iLowerOld := make(net.IP, len(targetFirstCopy)) - copy(iLowerOld, targetFirstCopy) + targetFirstCopy := slices.Clone(targetFirstIP) // Since golang only supports up to unsigned 64-bit integers, and we need // to perform addition on addresses, use math/big library, which allows @@ -258,12 +252,9 @@ func partitionCIDR(targetCIDR net.IPNet, excludeCIDR net.IPNet) ([]*net.IPNet, [ iUpper := big.NewInt(0) iLower = iLower.SetBytes(targetFirstCopy) - var bitLen int - + bitLen := ipv6BitLen if targetIsIPv4 { bitLen = ipv4BitLen - } else { - bitLen = ipv6BitLen } shiftAmount := (uint)(bitLen - newPrefixLen) @@ -297,7 +288,6 @@ func partitionCIDR(targetCIDR net.IPNet, excludeCIDR net.IPNet) ([]*net.IPNet, [ iUpperBytes = append(zeroBytes, iUpper.Bytes()...) } else { iUpperBytes = iUpper.Bytes() - } iLowerBytesLen := len(iLower.Bytes()) @@ -330,11 +320,9 @@ func partitionCIDR(targetCIDR net.IPNet, excludeCIDR net.IPNet) ([]*net.IPNet, [ iLower = iLower.Set(matched) iUpper = iUpper.Add(matched, big.NewInt(0).Lsh(big.NewInt(1), uint(bitLen-newPrefixLen))) - } - excludeList := []*net.IPNet{&excludeCIDR} - return left, excludeList, right + return left, right } func getNextIP(ip net.IP) net.IP { diff --git a/vendor/github.com/open-policy-agent/opa/internal/compiler/wasm/opa/callgraph.csv b/vendor/github.com/open-policy-agent/opa/internal/compiler/wasm/opa/callgraph.csv index 10dc4d482e..177aaee917 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/compiler/wasm/opa/callgraph.csv +++ b/vendor/github.com/open-policy-agent/opa/internal/compiler/wasm/opa/callgraph.csv @@ -636,11 +636,16 @@ opa_strings_replace,opa_realloc opa_strings_replace,memcpy opa_strings_replace,opa_string_allocated opa_strings_replace_n,opa_value_type +opa_strings_replace_n,opa_object_keys opa_strings_replace_n,opa_malloc +opa_strings_replace_n,memset +opa_strings_replace_n,opa_value_get +opa_strings_replace_n,opa_strncmp +opa_strings_replace_n,opa_realloc opa_strings_replace_n,memcpy opa_strings_replace_n,opa_string_allocated -opa_strings_replace_n,opa_strings_replace -opa_strings_replace_n,opa_value_free +opa_strings_replace_n,opa_free +opa_strings_replace_n,opa_array_free opa_strings_reverse,opa_value_type opa_strings_reverse,opa_malloc opa_strings_reverse,opa_unicode_decode_utf8 diff --git a/vendor/github.com/open-policy-agent/opa/internal/compiler/wasm/opa/opa.wasm b/vendor/github.com/open-policy-agent/opa/internal/compiler/wasm/opa/opa.wasm index 25d39a83e9..6be1842ddc 100644 Binary files a/vendor/github.com/open-policy-agent/opa/internal/compiler/wasm/opa/opa.wasm and b/vendor/github.com/open-policy-agent/opa/internal/compiler/wasm/opa/opa.wasm differ diff --git a/vendor/github.com/open-policy-agent/opa/internal/compiler/wasm/wasm.go b/vendor/github.com/open-policy-agent/opa/internal/compiler/wasm/wasm.go index b7f1a27812..95c02d9e22 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/compiler/wasm/wasm.go +++ b/vendor/github.com/open-policy-agent/opa/internal/compiler/wasm/wasm.go @@ -1026,9 +1026,7 @@ func (c *Compiler) compileBlock(block *ir.Block) ([]instruction.Instruction, err return nil, err } case *ir.CallDynamicStmt: - if err := c.compileCallDynamicStmt(stmt, &instrs); err != nil { - return nil, err - } + c.compileCallDynamicStmt(stmt, &instrs) case *ir.WithStmt: if err := c.compileWithStmt(stmt, &instrs); err != nil { return instrs, err @@ -1485,7 +1483,7 @@ func (c *Compiler) compileUpsert(local ir.Local, path []int, value ir.Operand, _ ) } -func (c *Compiler) compileCallDynamicStmt(stmt *ir.CallDynamicStmt, result *[]instruction.Instruction) error { +func (c *Compiler) compileCallDynamicStmt(stmt *ir.CallDynamicStmt, result *[]instruction.Instruction) { instrs := make([]instruction.Instruction, 0, 3+3*len(stmt.Path)+len(stmt.Args)+10) larray := c.genLocal() lidx := c.genLocal() @@ -1533,7 +1531,6 @@ func (c *Compiler) compileCallDynamicStmt(stmt *ir.CallDynamicStmt, result *[]in ) *result = append(*result, instrs...) - return nil } func (c *Compiler) compileCallStmt(stmt *ir.CallStmt, result *[]instruction.Instruction) error { diff --git a/vendor/github.com/open-policy-agent/opa/internal/deepcopy/deepcopy.go b/vendor/github.com/open-policy-agent/opa/internal/deepcopy/deepcopy.go index dc3a231bc1..b0e0bb2fb8 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/deepcopy/deepcopy.go +++ b/vendor/github.com/open-policy-agent/opa/internal/deepcopy/deepcopy.go @@ -4,17 +4,15 @@ package deepcopy +import "github.com/open-policy-agent/opa/v1/util" + // DeepCopy performs a recursive deep copy for nested slices/maps and // returns the copied object. Supports []any // and map[string]any only func DeepCopy(val any) any { switch val := val.(type) { case []any: - cpy := make([]any, len(val)) - for i := range cpy { - cpy[i] = DeepCopy(val[i]) - } - return cpy + return util.Map(val, DeepCopy) case map[string]any: return Map(val) default: diff --git a/vendor/github.com/open-policy-agent/opa/internal/future/filter_imports.go b/vendor/github.com/open-policy-agent/opa/internal/future/filter_imports.go index 27ca5559f1..c8d136c76b 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/future/filter_imports.go +++ b/vendor/github.com/open-policy-agent/opa/internal/future/filter_imports.go @@ -4,46 +4,36 @@ package future -import "github.com/open-policy-agent/opa/v1/ast" +import ( + "slices" + + "github.com/open-policy-agent/opa/v1/ast" +) // FilterFutureImports filters OUT any future imports from the passed slice of // `*ast.Import`s. func FilterFutureImports(imps []*ast.Import) []*ast.Import { - ret := []*ast.Import{} - for _, imp := range imps { - path := imp.Path.Value.(ast.Ref) - if !ast.FutureRootDocument.Equal(path[0]) { - ret = append(ret, imp) - } - } - return ret + return slices.DeleteFunc(slices.Clone(imps), isFutureKeywordImport) } // IsAllFutureKeywords returns true if the passed *ast.Import is `future.keywords` func IsAllFutureKeywords(imp *ast.Import) bool { path := imp.Path.Value.(ast.Ref) - return len(path) == 2 && - ast.FutureRootDocument.Equal(path[0]) && - path[1].Equal(ast.InternedTerm("keywords")) + return len(path) == 2 && path.HasPrefix(ast.FutureKeywordsRef) } // IsFutureKeyword returns true if the passed *ast.Import is `future.keywords.{kw}` func IsFutureKeyword(imp *ast.Import, kw string) bool { path := imp.Path.Value.(ast.Ref) - return len(path) == 3 && - ast.FutureRootDocument.Equal(path[0]) && - path[1].Equal(ast.InternedTerm("keywords")) && - path[2].Equal(ast.StringTerm(kw)) + return len(path) == 3 && path.HasPrefix(ast.FutureKeywordsRef) && path[2].Equal(ast.InternedTerm(kw)) } func WhichFutureKeyword(imp *ast.Import) (string, bool) { + name := imp.Name().String() + return name, imp.Alias == "" && IsFutureKeyword(imp, name) +} + +func isFutureKeywordImport(imp *ast.Import) bool { path := imp.Path.Value.(ast.Ref) - if len(path) == 3 && - ast.FutureRootDocument.Equal(path[0]) && - path[1].Equal(ast.InternedTerm("keywords")) { - if str, ok := path[2].Value.(ast.String); ok { - return string(str), true - } - } - return "", false + return len(path) > 0 && path.HasPrefix(ast.FutureKeywordsRef[:1]) } diff --git a/vendor/github.com/open-policy-agent/opa/internal/future/parser_opts.go b/vendor/github.com/open-policy-agent/opa/internal/future/parser_opts.go index eaeb87e296..76d30dc51d 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/future/parser_opts.go +++ b/vendor/github.com/open-policy-agent/opa/internal/future/parser_opts.go @@ -15,16 +15,14 @@ import ( // `ast.ParserOptions` that can be used to parse a statement according to the // included "future.keywords" and "future.keywords.xyz" imports. func ParserOptionsFromFutureImports(imports []*ast.Import) (ast.ParserOptions, error) { - popts := ast.ParserOptions{ - FutureKeywords: []string{}, - } + popts := ast.ParserOptions{} for _, imp := range imports { path := imp.Path.Value.(ast.Ref) if !ast.FutureRootDocument.Equal(path[0]) { continue } if len(path) >= 2 { - if string(path[1].Value.(ast.String)) != "keywords" { + if !path.HasPrefix(ast.FutureKeywordsRef) { return popts, fmt.Errorf("unknown future import: %v", imp) } if len(path) == 2 { diff --git a/vendor/github.com/open-policy-agent/opa/internal/planner/planner.go b/vendor/github.com/open-policy-agent/opa/internal/planner/planner.go index e5a62a2e04..558d71da9f 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/planner/planner.go +++ b/vendor/github.com/open-policy-agent/opa/internal/planner/planner.go @@ -52,6 +52,7 @@ type Planner struct { allRules map[*ast.Rule]bool // all rules parsed from input modules, used to track unplanned rules for additional reporting (e.g. coverage) plannedRules map[*ast.Rule]bool + planning map[string]struct{} // ground path prefixes currently being planned unplannedRules bool // whether to populate policy.UnplannedRules } @@ -91,6 +92,7 @@ func New() *Planner { allRules: map[*ast.Rule]bool{}, plannedRules: map[*ast.Rule]bool{}, + planning: map[string]struct{}{}, } } @@ -133,18 +135,13 @@ func (p *Planner) WithUnplannedRules(yes bool) *Planner { // Plan returns a IR plan for the policy query. func (p *Planner) Plan() (*ir.Policy, error) { - - if err := p.buildFunctrie(); err != nil { - return nil, err - } + p.buildFunctrie() if err := p.planQueries(); err != nil { return nil, err } - if err := p.planExterns(); err != nil { - return nil, err - } + p.planExterns() if p.unplannedRules { p.buildUnplannedRules() @@ -172,8 +169,7 @@ func (p *Planner) buildUnplannedRules() { }) } -func (p *Planner) buildFunctrie() error { - +func (p *Planner) buildFunctrie() { for _, module := range p.modules { // Create functrie node for empty packages so that extent queries return @@ -200,7 +196,6 @@ func (p *Planner) buildFunctrie() error { val.children = nil } } - return nil } func (p *Planner) planRules(rules []*ast.Rule) (string, error) { @@ -252,6 +247,23 @@ func (p *Planner) planRules(rules []*ast.Rule) (string, error) { return funcName, nil } + // One function is planned per ground path prefix, so rules whose refs only + // differ past a variable share a function. A reference from one of those + // rule bodies back into the same prefix is not recursion the compiler would + // reject, but the planner has no way to evaluate part of a function that is + // still being planned. The generation is left out of the key on purpose: a + // 'with' statement that shadows planned functions bumps it, and keying on + // it would let the same prefix re-enter planning forever. + if _, ok := p.planning[path]; ok { + err := fmt.Errorf("reference to %v is not supported: rules sharing that path prefix are planned as a single function", path) + if p.loc != nil { + return "", fmt.Errorf("%v: %w", p.loc, err) + } + return "", err + } + p.planning[path] = struct{}{} + defer delete(p.planning, path) + // Save current state of planner. // // TODO(tsandall): perhaps we would be better off using stacks here or @@ -580,7 +592,6 @@ func (p *Planner) planFuncParams(params []ir.Local, args ast.Args, idx int, iter } func (p *Planner) planQueries() error { - for _, qs := range p.queries { // Initialize the plan with a block that prepares the query result. @@ -663,7 +674,6 @@ func (p *Planner) planQueries() error { } func (p *Planner) planQuery(q ast.Body, index int, iter planiter) error { - if index >= len(q) { return iter() } @@ -2487,7 +2497,7 @@ func (p *Planner) planTermSliceRec(terms []*ast.Term, locals []ir.Operand, index }) } -func (p *Planner) planExterns() error { +func (p *Planner) planExterns() { p.policy.Static.BuiltinFuncs = make([]*ir.BuiltinFunc, 0, len(p.externs)) for name, decl := range p.externs { @@ -2497,8 +2507,6 @@ func (p *Planner) planExterns() error { slices.SortFunc(p.policy.Static.BuiltinFuncs, func(a, b *ir.BuiltinFunc) int { return strings.Compare(a.Name, b.Name) }) - - return nil } func (p *Planner) getStringConst(s string) int { diff --git a/vendor/github.com/open-policy-agent/opa/internal/semver/semver.go b/vendor/github.com/open-policy-agent/opa/internal/semver/semver.go index 61ba1ac42e..9c1196b71a 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/semver/semver.go +++ b/vendor/github.com/open-policy-agent/opa/internal/semver/semver.go @@ -45,13 +45,17 @@ type Version struct { func Parse(version string) (v Version, err error) { version = strings.TrimPrefix(version, "v") - version, v.Metadata = cut(version, '+') - if v.Metadata != "" && !reMetaIdentifier.MatchString(v.Metadata) { + var foundMetadata bool + + version, v.Metadata, foundMetadata = strings.Cut(version, "+") + if foundMetadata && !reMetaIdentifier.MatchString(v.Metadata) { return v, fmt.Errorf("invalid metadata identifier: %s", v.Metadata) } - version, v.PreRelease = cut(version, '-') - if v.PreRelease != "" && !reMetaIdentifier.MatchString(v.PreRelease) { + var foundPreRelease bool + + version, v.PreRelease, foundPreRelease = strings.Cut(version, "-") + if foundPreRelease && (!reMetaIdentifier.MatchString(v.PreRelease) || !validPreRelease(v.PreRelease)) { return v, fmt.Errorf("invalid pre-release identifier: %s", v.PreRelease) } @@ -59,19 +63,43 @@ func Parse(version string) (v Version, err error) { return v, fmt.Errorf("%s should contain major, minor, and patch versions", version) } - major, after := cut(version, '.') - if v.Major, err = strconv.ParseInt(major, 10, 64); err != nil { - return v, err + major, after := cutDot(version) + if v.Major, err = parseNumeric(major); err != nil { + return v, fmt.Errorf("invalid major version: %w", err) + } + + minor, after := cutDot(after) + if v.Minor, err = parseNumeric(minor); err != nil { + return v, fmt.Errorf("invalid minor version: %w", err) } - minor, after := cut(after, '.') - if v.Minor, err = strconv.ParseInt(minor, 10, 64); err != nil { - return v, err + if v.Patch, err = parseNumeric(after); err != nil { + return v, fmt.Errorf("invalid patch version: %w", err) } - v.Patch, err = strconv.ParseInt(after, 10, 64) + return v, nil +} + +// parseNumeric parses a major, minor or patch identifier, rejecting the empty +// string, a sign or a leading zero (all forbidden by SemVer 2.0.0) before +// converting to int64. +func parseNumeric(s string) (int64, error) { + if s == "" || s[0] == '+' || s[0] == '-' || (len(s) > 1 && s[0] == '0') { + return 0, fmt.Errorf("%q is not a valid numeric identifier", s) + } + return strconv.ParseInt(s, 10, 64) +} - return v, err +// validPreRelease reports whether every numeric pre-release identifier is free +// of leading zeroes, as required by SemVer 2.0.0. The identifier character set +// has already been checked by reMetaIdentifier. +func validPreRelease(pre string) bool { + for id := range strings.SplitSeq(pre, ".") { + if len(id) > 1 && id[0] == '0' && isAllDecimals(id) { + return false + } + } + return true } // MustParse is like Parse but panics if the version string is invalid instead of returning an error. @@ -162,8 +190,8 @@ func (v Version) Compare(other Version) int { return -1 } - a, afterA := cut(v.PreRelease, '.') - b, afterB := cut(other.PreRelease, '.') + a, afterA := cutDot(v.PreRelease) + b, afterB := cutDot(other.PreRelease) for { if a == "" && b != "" { @@ -209,8 +237,8 @@ func (v Version) Compare(other Version) int { return -1 } - a, afterA = cut(afterA, '.') - b, afterB = cut(afterB, '.') + a, afterA = cutDot(afterA) + b, afterB = cutDot(afterB) } } @@ -235,10 +263,13 @@ func length(v Version) int { return n } -// cut is a *slightly* faster version of strings.Cut only accepting -// single byte separators, and skipping the boolean return value. -func cut(s string, sep byte) (before, after string) { - if i := strings.IndexByte(s, sep); i >= 0 { +// cutDot is a *slightly* faster version of strings.Cut for the '.' separator, +// skipping the boolean return value. strings.Cut looks the separator up with +// strings.Index, which costs ~12% on BenchmarkCompare next to IndexByte. +// +//nolint:modernize // stringscut: measurably slower here, see above. +func cutDot(s string) (before, after string) { + if i := strings.IndexByte(s, '.'); i >= 0 { return s[:i], s[i+1:] } return s, "" diff --git a/vendor/github.com/open-policy-agent/opa/internal/yaml/yaml.go b/vendor/github.com/open-policy-agent/opa/internal/yaml/yaml.go new file mode 100644 index 0000000000..9f6e53a57f --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/internal/yaml/yaml.go @@ -0,0 +1,340 @@ +// Copyright 2026 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package yaml provides YAML <-> JSON conversion for OPA, on top of +// go.yaml.in/yaml/v3. +// +// It replaces sigs.k8s.io/yaml, which is pinned to go.yaml.in/yaml/v2 and +// therefore resolves YAML 1.1 boolean spellings (on/off/yes/no) in positions +// where the YAML 1.2 core schema calls for strings. +package yaml + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "reflect" + "strconv" + + "go.yaml.in/yaml/v3" +) + +// Marshal serializes obj as YAML. obj is first round-tripped through +// encoding/json so that `json` struct tags and json.Marshaler +// implementations are honoured, matching the behaviour callers relied on +// from sigs.k8s.io/yaml. +func Marshal(obj any) ([]byte, error) { + bs, err := json.Marshal(obj) + if err != nil { + return nil, fmt.Errorf("error marshaling into JSON: %w", err) + } + var jsonObj any + if err := yaml.Unmarshal(bs, &jsonObj); err != nil { + return nil, err + } + return marshalYAML(jsonObj) +} + +// marshalYAML emits YAML at 2-space indentation. go-yaml v3 defaults to 4, +// where sigs.k8s.io/yaml (on go-yaml v2) emitted 2. +func marshalYAML(obj any) ([]byte, error) { + var buf bytes.Buffer + enc := yaml.NewEncoder(&buf) + enc.SetIndent(2) + if err := enc.Encode(obj); err != nil { + _ = enc.Close() + return nil, err + } + if err := enc.Close(); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// JSONOpt configures the encoding/json decoder used by Unmarshal. +type JSONOpt func(*json.Decoder) *json.Decoder + +// Unmarshal decodes a YAML document into obj, using encoding/json semantics +// (`json` struct tags, json.Unmarshaler) rather than go-yaml's. +func Unmarshal(bs []byte, obj any, opts ...JSONOpt) error { + js, err := YAMLToJSON(bs) + if err != nil { + return err + } + d := json.NewDecoder(bytes.NewReader(js)) + for _, opt := range opts { + d = opt(d) + } + if err := d.Decode(obj); err != nil { + return fmt.Errorf("error unmarshaling JSON: %w", err) + } + return nil +} + +// YAMLToJSON converts a single YAML document to JSON. Input holding more than +// one document keeps its historical meaning - only the first is converted - +// but the rest of the stream still has to parse. +func YAMLToJSON(bs []byte) ([]byte, error) { + node, err := firstDocument(bs) + if err != nil { + return nil, err + } + + var obj any + if node != nil { + normalize(node, map[*yaml.Node]struct{}{}) + if err := node.Decode(&obj); err != nil { + return nil, err + } + } + + obj, err = jsonable(obj) + if err != nil { + return nil, err + } + return json.Marshal(obj) +} + +// firstDocument returns the first document in bs, or nil if bs holds none. +// +// The remaining documents are parsed and discarded. go-yaml stops reading at +// the end of the first document, so without this a syntax error further into +// the input is never reported: `" a:\nb: 1"` closes the mapping at the dedent +// and silently drops `b: 1`, rather than failing the way the YAML spec calls +// for (issue 6854). +func firstDocument(bs []byte) (*yaml.Node, error) { + dec := yaml.NewDecoder(bytes.NewReader(bs)) + + var first *yaml.Node + for { + var node yaml.Node + if err := dec.Decode(&node); err != nil { + if errors.Is(err, io.EOF) { + return first, nil + } + return nil, err + } + if first == nil { + first = &node + } + } +} + +// normalize rewrites the node tree before it is decoded, so that documents +// go-yaml v2 accepted keep working under v3. +// +// Implicitly resolved !!timestamp scalars are re-tagged !!str. !!timestamp is +// a YAML 1.1 type that go-yaml v3 still resolves in the core schema; leaving +// it in place would silently rewrite `2023-01-01` to `2023-01-01T00:00:00Z` +// on the way to JSON. +// +// Repeated merge keys are folded into the sequence form, and duplicate +// mapping keys are collapsed to the last occurrence. go-yaml v3 rejects both +// outright; go-yaml v2 accepted them, and turning documents that load today +// into hard errors is a bigger change than this package is trying to make. +// +// Anchors make the node graph a DAG, so visited guards against re-walking a +// shared subtree. +func normalize(n *yaml.Node, visited map[*yaml.Node]struct{}) { + if n == nil { + return + } + if _, ok := visited[n]; ok { + return + } + visited[n] = struct{}{} + + switch n.Kind { + case yaml.ScalarNode: + if n.Tag == "!!timestamp" && n.Style == 0 { + n.Tag = "!!str" + } + case yaml.MappingNode: + n.Content = collapseMergeKeys(n.Content) + n.Content = dedupeKeys(n.Content) + } + + normalize(n.Alias, visited) + for _, c := range n.Content { + normalize(c, visited) + } +} + +// collapseMergeKeys rewrites a mapping that repeats `<<` into the spec's +// sequence form (`<<: [a, b]`), which go-yaml v3 accepts. go-yaml v2 applied +// repeated merge keys in document order, so preserve that order. +func collapseMergeKeys(content []*yaml.Node) []*yaml.Node { + first := -1 + var merged []*yaml.Node + + for i := 0; i+1 < len(content); i += 2 { + if content[i].Kind != yaml.ScalarNode || content[i].Tag != "!!merge" { + continue + } + if first < 0 { + first = i + } + if v := content[i+1]; v.Kind == yaml.SequenceNode { + merged = append(merged, v.Content...) + } else { + merged = append(merged, v) + } + } + + if first < 0 || len(merged) < 2 { + return content + } + + out := make([]*yaml.Node, 0, len(content)) + for i := 0; i+1 < len(content); i += 2 { + switch { + case i == first: + out = append(out, content[i], &yaml.Node{ + Kind: yaml.SequenceNode, + Tag: "!!seq", + Content: merged, + }) + case content[i].Kind == yaml.ScalarNode && content[i].Tag == "!!merge": + // dropped; folded into the sequence above + default: + out = append(out, content[i], content[i+1]) + } + } + return out +} + +// dedupeKeys drops all but the last occurrence of each key in a mapping's +// flattened key/value Content slice, preserving the position of the first +// occurrence the way a last-wins map assignment would. +// +// go-yaml v3 rejects duplicate keys outright, but go-yaml v2 accepted them, +// and turning documents that load today into hard errors is a bigger change +// than this package is trying to make. Keys are compared by the string they +// will occupy in the resulting JSON object, so `1` and `"1"` collide here the +// same way they would there. +func dedupeKeys(content []*yaml.Node) []*yaml.Node { + seen := make(map[string]int, len(content)/2) + dropped := false + + for i := 0; i+1 < len(content); i += 2 { + k := content[i] + // Merge keys are not real keys, and non-scalar keys have no JSON + // representation - both are handled elsewhere. + if k.Kind != yaml.ScalarNode || k.Tag == "!!merge" { + continue + } + var kv any + if err := k.Decode(&kv); err != nil { + continue + } + id, ok := keyString(kv) + if !ok { + continue + } + if prevVal, ok := seen[id]; ok { + // Keep the earlier key node's position, take the later value. + content[prevVal] = content[i+1] + content[i], content[i+1] = nil, nil + dropped = true + continue + } + seen[id] = i + 1 + } + + if !dropped { + return content + } + + out := content[:0] + for _, n := range content { + if n != nil { + out = append(out, n) + } + } + return out +} + +// JSONToYAML converts JSON to YAML, preserving nothing but the value. +func JSONToYAML(bs []byte) ([]byte, error) { + var obj any + // json.Number would be re-encoded as a quoted string by go-yaml, so decode + // numbers as float64 the way encoding/json does by default. + if err := json.Unmarshal(bs, &obj); err != nil { + return nil, err + } + return marshalYAML(obj) +} + +// jsonable rewrites the result of a go-yaml decode into something +// encoding/json can marshal: YAML permits mapping keys of any type, JSON +// only permits strings. +func jsonable(x any) (any, error) { + switch x := x.(type) { + case map[string]any: + for k, v := range x { + v, err := jsonable(v) + if err != nil { + return nil, err + } + x[k] = v + } + return x, nil + case map[any]any: + out := make(map[string]any, len(x)) + for k, v := range x { + ks, ok := keyString(k) + if !ok { + return nil, fmt.Errorf("unsupported map key of type: %s, key: %+#v, value: %+#v", reflect.TypeOf(k), k, v) + } + v, err := jsonable(v) + if err != nil { + return nil, err + } + out[ks] = v + } + return out, nil + case []any: + for i, v := range x { + v, err := jsonable(v) + if err != nil { + return nil, err + } + x[i] = v + } + return x, nil + default: + return x, nil + } +} + +func keyString(k any) (string, bool) { + switch k := k.(type) { + case string: + return k, true + case int: + return strconv.Itoa(k), true + case int64: + return strconv.FormatInt(k, 10), true + case uint64: + return strconv.FormatUint(k, 10), true + case float64: + // Match how go-yaml renders floats when marshaling. + switch s := strconv.FormatFloat(k, 'g', -1, 32); s { + case "+Inf": + return ".inf", true + case "-Inf": + return "-.inf", true + case "NaN": + return ".nan", true + default: + return s, true + } + case bool: + return strconv.FormatBool(k), true + default: + return "", false + } +} diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/annotations.go b/vendor/github.com/open-policy-agent/opa/v1/ast/annotations.go index 1aaf9680c7..0b25692602 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/annotations.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/annotations.go @@ -95,6 +95,14 @@ func (a *Annotations) String() string { return string(bs) } +func (a *Annotations) AppendText(buf []byte) ([]byte, error) { + bs, err := a.MarshalJSON() + if err == nil { + buf = append(buf, bs...) + } + return buf, err +} + // Loc returns the location of this annotation. func (a *Annotations) Loc() *Location { return a.Location diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/builtins.go b/vendor/github.com/open-policy-agent/opa/v1/ast/builtins.go index 1f92cadd1d..8942e4f34c 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/builtins.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/builtins.go @@ -3126,6 +3126,8 @@ var JSONSchemaVerify = &Builtin{ Description("`output` is of the form `[valid, error]`. If the schema is valid, then `valid` is `true`, and `error` is `null`. Otherwise, `valid` is `false` and `error` is a string describing the error."), ), Categories: objectCat, + // `$ref`s are dereferenced at evaluation time, so the result depends on what those URLs serve. + Nondeterministic: true, // Needs the BuiltinContext to read the allow_net capability, which // restricts the hosts that remote `$ref`s may be fetched from. CanSkipBctx: false, @@ -3159,8 +3161,10 @@ var JSONMatchSchema = &Builtin{ }, nil)). Description("`output` is of the form `[match, errors]`. If the document is valid given the schema, then `match` is `true`, and `errors` is an empty array. Otherwise, `match` is `false` and `errors` is an array of objects describing the error(s)."), ), - Categories: objectCat, - CanSkipBctx: false, + Categories: objectCat, + // `$ref`s are dereferenced at evaluation time, so the result depends on what those URLs serve. + Nondeterministic: true, + CanSkipBctx: false, } /** diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/capabilities.go b/vendor/github.com/open-policy-agent/opa/v1/ast/capabilities.go index e804147d69..610de4bc54 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/capabilities.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/capabilities.go @@ -5,7 +5,6 @@ package ast import ( - "bytes" _ "embed" "encoding/json" "fmt" @@ -18,10 +17,51 @@ import ( "github.com/open-policy-agent/opa/internal/semver" "github.com/open-policy-agent/opa/internal/wasm/sdk/opa/capabilities" + "github.com/open-policy-agent/opa/v1/ast/internal/tokens" caps "github.com/open-policy-agent/opa/v1/capabilities" "github.com/open-policy-agent/opa/v1/util" ) +// In the compiler, we used this to check that we're OK working with ref heads. +// If this isn't present, we'll fail. This is to ensure that older versions of +// OPA can work with policies that we're compiling -- if they don't know ref +// heads, they wouldn't be able to parse them. +const ( + FeatureRefHeadStringPrefixes = "rule_head_ref_string_prefixes" + FeatureRefHeads = "rule_head_refs" + FeatureRegoV1 = "rego_v1" + FeatureRegoV1Import = "rego_v1_import" + FeatureKeywordsInRefs = "keywords_in_refs" + FeatureTemplateStrings = "template_strings" +) + +var ( + // Features carries the default features supported by this version of OPA. + // Use RegisterFeatures to add to them. + Features = []string{ + FeatureRegoV1, + FeatureKeywordsInRefs, + FeatureTemplateStrings, + } + v0v1compatFeatures = []string{ + FeatureRefHeadStringPrefixes, + FeatureRefHeads, + FeatureRegoV1Import, + FeatureRegoV1, // Included in v0 capabilities to allow v1 bundles in --v0-compatible mode + FeatureKeywordsInRefs, + } + // NOTE(tsandall): this file is generated by internal/cmd/genversionindex/main.go + // and run as part of go:generate. We generate the version index as part of the + // build process because it's relatively expensive to build (it takes ~500ms on + // my machine) and never changes. + // + //go:embed version_index.json + versionIndexBs []byte + // init only on demand, as JSON unmarshalling comes with some cost, and contributes + // noise to things like pprof stats + minVersionIndexOnce = sync.OnceValue(minVersionIndex) +) + // VersonIndex contains an index from built-in function name, language feature, // and future rego keyword to version number. During the build, this is used to // create an index of the minimum version required for the built-in/feature/kw. @@ -31,51 +71,13 @@ type VersionIndex struct { Keywords map[string]semver.Version `json:"keywords"` } -// NOTE(tsandall): this file is generated by internal/cmd/genversionindex/main.go -// and run as part of go:generate. We generate the version index as part of the -// build process because it's relatively expensive to build (it takes ~500ms on -// my machine) and never changes. -// -//go:embed version_index.json -var versionIndexBs []byte - -// init only on demand, as JSON unmarshalling comes with some cost, and contributes -// noise to things like pprof stats -var minVersionIndexOnce = sync.OnceValue(func() VersionIndex { - var vi VersionIndex - if err := json.Unmarshal(versionIndexBs, &vi); err != nil { - panic(err) - } - return vi -}) - -// In the compiler, we used this to check that we're OK working with ref heads. -// If this isn't present, we'll fail. This is to ensure that older versions of -// OPA can work with policies that we're compiling -- if they don't know ref -// heads, they wouldn't be able to parse them. -const FeatureRefHeadStringPrefixes = "rule_head_ref_string_prefixes" -const FeatureRefHeads = "rule_head_refs" -const FeatureRegoV1 = "rego_v1" -const FeatureRegoV1Import = "rego_v1_import" -const FeatureKeywordsInRefs = "keywords_in_refs" -const FeatureTemplateStrings = "template_strings" - -// Features carries the default features supported by this version of OPA. -// Use RegisterFeatures to add to them. -var Features = []string{ - FeatureRegoV1, - FeatureKeywordsInRefs, - FeatureTemplateStrings, -} - // RegisterFeatures lets applications wrapping OPA register features, to be // included in `ast.CapabilitiesForThisVersion()`. func RegisterFeatures(fs ...string) { for i := range fs { - if slices.Contains(Features, fs[i]) { - continue + if !slices.Contains(Features, fs[i]) { + Features = append(Features, fs[i]) } - Features = append(Features, fs[i]) } } @@ -113,6 +115,9 @@ type CapabilitiesOptions struct { } func newCapabilitiesOptions(opts []CapabilitiesOption) CapabilitiesOptions { + if len(opts) == 0 { + return CapabilitiesOptions{} + } co := CapabilitiesOptions{} for _, opt := range opts { opt(&co) @@ -146,59 +151,35 @@ func CapabilitiesExperimentalKeywords(yes bool) CapabilitiesOption { // CapabilitiesForThisVersion returns the capabilities of this version of OPA. func CapabilitiesForThisVersion(opts ...CapabilitiesOption) *Capabilities { co := newCapabilitiesOptions(opts) - - f := &Capabilities{} + f := &Capabilities{Builtins: util.SortedFunc(slices.Clone(Builtins), cmpBuiltinName)} for _, vers := range capabilities.ABIVersions() { f.WasmABIVersions = append(f.WasmABIVersions, WasmABIVersion{Version: vers[0], Minor: vers[1]}) } - f.Builtins = make([]*Builtin, len(Builtins)) - copy(f.Builtins, Builtins) - - slices.SortFunc(f.Builtins, func(a, b *Builtin) int { - return strings.Compare(a.Name, b.Name) - }) - switch co.regoVersion { case RegoV0, RegoV0CompatV1: - for kw := range allFutureKeywords { - if _, internal := experimentalFutureKeywords[kw]; internal && !co.experimentalKeywords { - continue - } - f.FutureKeywords = append(f.FutureKeywords, kw) - } - - f.Features = []string{ - FeatureRefHeadStringPrefixes, - FeatureRefHeads, - FeatureRegoV1Import, - FeatureRegoV1, // Included in v0 capabilities to allow v1 bundles in --v0-compatible mode - FeatureKeywordsInRefs, - } + f.FutureKeywords = co.filterFutureKeywords(allFutureKeywords) + f.Features = util.Sorted(slices.Clone(v0v1compatFeatures)) default: - for kw := range futureKeywords { - if _, internal := experimentalFutureKeywords[kw]; internal && !co.experimentalKeywords { - continue - } - f.FutureKeywords = append(f.FutureKeywords, kw) - } - - f.Features = make([]string, len(Features)) - copy(f.Features, Features) + f.FutureKeywords = co.filterFutureKeywords(futureKeywords) + f.Features = util.Sorted(slices.Clone(Features)) } - slices.Sort(f.FutureKeywords) - slices.Sort(f.Features) - return f } +func (co *CapabilitiesOptions) filterFutureKeywords(src map[string]tokens.Token) []string { + if co.experimentalKeywords { + return util.KeysSorted(src) + } + return slices.DeleteFunc(util.KeysSorted(src), isExperimental) +} + // LoadCapabilitiesJSON loads a JSON serialized capabilities structure from the reader r. func LoadCapabilitiesJSON(r io.Reader) (*Capabilities, error) { - d := util.NewJSONDecoder(r) var c Capabilities - return &c, d.Decode(&c) + return &c, util.NewJSONDecoder(r).Decode(&c) } // LoadCapabilitiesVersion loads a JSON serialized capabilities structure from the specific version. @@ -208,18 +189,16 @@ func LoadCapabilitiesVersion(version string) (*Capabilities, error) { return nil, err } - for _, cv := range cvs { - if cv == version { - cont, err := caps.FS.ReadFile(cv + ".json") - if err != nil { - return nil, err - } - - return LoadCapabilitiesJSON(bytes.NewReader(cont)) + if slices.Contains(cvs, version) { + fd, err := caps.FS.Open(version + ".json") + if err != nil { + return nil, err } - + defer fd.Close() + return LoadCapabilitiesJSON(fd) } - return nil, fmt.Errorf("no capabilities version found %v", version) + + return nil, fmt.Errorf("no capabilities version found %s", version) } // LoadCapabilitiesFile loads a JSON serialized capabilities structure from a file. @@ -234,12 +213,11 @@ func LoadCapabilitiesFile(file string) (*Capabilities, error) { // LoadCapabilitiesVersions loads all capabilities versions func LoadCapabilitiesVersions() ([]string, error) { - ents, err := caps.FS.ReadDir(".") + entries, err := caps.FS.ReadDir(".") if err != nil { return nil, err } - - return util.SortedStableFunc(util.Map(ents, removeJsonSuffix), semver.Compare), nil + return util.SortedStableFunc(util.Map(entries, removeJsonSuffix), semver.Compare), nil } // MinimumCompatibleVersion returns the minimum compatible OPA version based on @@ -307,6 +285,13 @@ func (c *Capabilities) addBuiltinSorted(bi *Builtin) { c.Builtins[i] = bi } +func minVersionIndex() (vi VersionIndex) { + if err := json.Unmarshal(versionIndexBs, &vi); err != nil { + panic(err) + } + return vi +} + func cmpBuiltinName(a, b *Builtin) int { return strings.Compare(a.Name, b.Name) } @@ -314,3 +299,8 @@ func cmpBuiltinName(a, b *Builtin) int { func removeJsonSuffix(ent fs.DirEntry) string { return strings.Replace(ent.Name(), ".json", "", 1) } + +func isExperimental(kw string) bool { + _, experimental := experimentalFutureKeywords[kw] + return experimental +} diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/check.go b/vendor/github.com/open-policy-agent/opa/v1/ast/check.go index c07130d205..05c7f889aa 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/check.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/check.go @@ -44,7 +44,14 @@ func newTypeChecker() *typeChecker { func (tc *typeChecker) newEnv(exist *TypeEnv) *TypeEnv { if exist != nil { - return exist.wrap() + env := exist.wrap() + // The wrapped environment would otherwise inherit exist's checker + // factory, which may have been built with a different configuration + // than tc -- the compiler seeds Compiler.TypeEnv from a bare checker, + // for one. Comprehension bodies are typed lazily through this factory, + // so it has to reflect the checker that is running now. + env.newChecker = tc.copyForEnv + return env } env := newTypeEnv(tc.copy) if tc.input != nil { @@ -53,6 +60,14 @@ func (tc *typeChecker) newEnv(exist *TypeEnv) *TypeEnv { return env } +// copyForEnv returns a checker for typing the closures an environment is asked +// about. It drops the required-capabilities accumulator: environments outlive +// the compilation that produced them, and the builtins in those closures are +// already recorded by checkClosures. +func (tc *typeChecker) copyForEnv() *typeChecker { + return tc.copy().WithRequiredCapabilities(nil) +} + func (tc *typeChecker) copy() *typeChecker { return newTypeChecker(). WithVarRewriter(tc.varRewriter). @@ -246,7 +261,6 @@ func (tc *typeChecker) getSchemaType(schemaAnnot *SchemaAnnotation, rule *Rule) } func (tc *typeChecker) checkRule(env *TypeEnv, as *AnnotationSet, rule *Rule) { - env = env.wrap() schemaAnnots := getRuleAnnotation(as, rule) @@ -307,7 +321,16 @@ func (tc *typeChecker) checkRule(env *TypeEnv, as *AnnotationSet, rule *Rule) { args[i] = cpy.GetByValue(rule.Head.Args[i].Value) } - tpe = types.NewFunction(args, cpy.GetByValue(rule.Head.Value.Value)) + result := cpy.GetByValue(rule.Head.Value.Value) + if result == nil && tc.allowUndefinedFuncs { + // The value is only unknown because it came out of a call to an + // undefined function. Recording a function type without a result + // would make callers look like they pass one argument too many, so + // fall back to any. + result = types.A + } + + tpe = types.NewFunction(args, result) } else { switch rule.Head.RuleKind() { case SingleValue: @@ -357,23 +380,17 @@ func nestedObject(env *TypeEnv, path Ref, tpe types.Type) (types.Type, error) { return tpe, nil } - k := path[0] typeV, err := nestedObject(env, path[1:], tpe) - if err != nil { + if err != nil || typeV == nil { return nil, err } - if typeV == nil { - return nil, nil - } - var dynamicProperty *types.DynamicProperty - typeK := env.GetByValue(k.Value) + typeK := env.GetByValue(path[0].Value) if typeK == nil { return nil, nil } - dynamicProperty = types.NewDynamicProperty(typeK, typeV) - return types.NewObject(nil, dynamicProperty), nil + return types.NewObject(nil, types.NewDynamicProperty(typeK, typeV)), nil } func (tc *typeChecker) checkExpr(env *TypeEnv, expr *Expr) *Error { @@ -395,13 +412,75 @@ func (tc *typeChecker) checkExpr(env *TypeEnv, expr *Expr) *Error { } } - if operator == "eq" { + switch operator { + case Equality.Name: return checkExprEq(env, expr) + case Member.Name, MemberWithKey.Name: + if err := checkExprMember(env, expr, operator == MemberWithKey.Name); err != nil { + return err + } } return tc.checkExprBuiltin(env, expr) } +// checkExprMember type checks the `in` operator, whose operands are declared as +// any: what may be found in a collection depends on the collection's own type, +// which a function declaration can't express. +func checkExprMember(env *TypeEnv, expr *Expr, withKey bool) *Error { + arity := Member.Decl.Arity() + if withKey { + arity = MemberWithKey.Decl.Arity() + } + + args := expr.Operands() + if len(args) < arity { + return nil // too few arguments; reported by checkExprBuiltin + } + + collection := env.GetByValue(args[arity-1].Value) + + // `in` yields false rather than erroring for operands it can't enumerate. + values := types.Values(collection) + if values == nil { + return nil + } + + if withKey { + if err := checkExprMemberOperand(env, expr, args[0], types.Keys(collection)); err != nil { + return err + } + } + + return checkExprMemberOperand(env, expr, args[arity-2], values) +} + +// checkExprMemberOperand checks that term can occur in the collection being +// searched, inferring the type of untyped terms (e.g. `some x in xs`) as it goes. +func checkExprMemberOperand(env *TypeEnv, expr *Expr, term *Term, tpe types.Type) *Error { + if tpe == nil || types.Nil(tpe) { + return nil + } + + have := env.GetByValue(term.Value) + + // unifies rejects already-typed terms; unify1 infers types for untyped vars + // and checks the resolved parts of partially typed composites. + if (!types.Nil(have) && !unifies(have, tpe)) || !unify1(env, term, tpe, false) { + return &Error{ + Code: TypeErr, + Location: expr.Location, + Message: "match error", + Details: &UnificationErrDetail{ + Left: have, + Right: tpe, + }, + } + } + + return nil +} + func (tc *typeChecker) checkExprBuiltin(env *TypeEnv, expr *Expr) *Error { // NOTE(tsandall): undefined functions will have been caught earlier in the // compiler. We check for undefined functions before the safety check so @@ -477,14 +556,16 @@ func (tc *typeChecker) checkExprBuiltin(env *TypeEnv, expr *Expr) *Error { } func checkExprEq(env *TypeEnv, expr *Expr) *Error { + ops := expr.Operands() + num := len(ops) - pre := getArgTypes(env, expr.Operands()) - - if len(pre) < Equality.Decl.Arity() { + if num < Equality.Decl.Arity() { + pre := getArgTypes(env, ops) return newArgError(expr.Location, expr.Operator(), "too few arguments", pre, Equality.Decl.FuncArgs()) } - if Equality.Decl.Arity() < len(pre) { + if Equality.Decl.Arity() < num { + pre := getArgTypes(env, ops) return newArgError(expr.Location, expr.Operator(), "too many arguments", pre, Equality.Decl.FuncArgs()) } @@ -896,7 +977,6 @@ func (rc *refChecker) checkApply(curr *TypeEnv, ref Ref) *Error { } func (rc *refChecker) checkRef(curr *TypeEnv, node *typeTreeNode, ref Ref, idx int) *Error { - if idx == len(ref) { return nil } @@ -963,6 +1043,13 @@ func (rc *refChecker) checkRefLeaf(tpe types.Type, ref Ref, idx int) *Error { head := ref[idx] + if isEmptyCollectionType(tpe) { + // The collection has no members at all, so nothing can be selected from + // it. Report that like any other missing key rather than as a value that + // can't be dereferenced at all. + return newRefErrInvalid(ref[0].Location, rc.varRewriter(ref), idx, nil, nil, nil) + } + keys := types.Keys(tpe) if keys == nil { return newRefErrUnsupported(ref[0].Location, rc.varRewriter(ref), idx-1, tpe) @@ -1002,6 +1089,27 @@ func (rc *refChecker) checkRefLeaf(tpe types.Type, ref Ref, idx int) *Error { return rc.checkRefLeaf(types.Values(tpe), ref, idx+1) } +// isEmptyCollectionType returns true if tpe is the type of a collection that +// can hold nothing: an object with neither static nor dynamic properties, an +// array with no items, or a set with no element type. +func isEmptyCollectionType(tpe types.Type) bool { + if named, ok := tpe.(*types.NamedType); ok { + tpe = named.Type + } + if rec, ok := tpe.(*types.Recursive); ok { + tpe = rec.Unwrap() + } + switch tpe := tpe.(type) { + case *types.Object: + return len(tpe.StaticProperties()) == 0 && tpe.DynamicProperties() == nil + case *types.Array: + return tpe.Len() == 0 && tpe.Dynamic() == nil + case *types.Set: + return tpe.Of() == nil + } + return false +} + // unifies checks whether two types are compatible with each other. func unifies(a, b types.Type) bool { @@ -1065,6 +1173,11 @@ func unifies(a, b types.Type) bool { if !ok { return false } + // A set type without an element type is the empty set, which is a + // member of every set type. + if a.Of() == nil || b.Of() == nil { + return true + } return unifies(types.Values(a), types.Values(b)) case *types.Function: // NOTE(sr): variadic functions can only be internal ones, and we've forbidden @@ -1172,10 +1285,42 @@ type ArgErrDetail struct { // Lines returns the string representation of the detail. func (d *ArgErrDetail) Lines() []string { - lines := make([]string, 2) - lines[0] = "have: " + formatArgs(d.Have) - lines[1] = "want: " + d.Want.String() - return lines + have := "have: " + formatArgs(d.Have) + want := "want: " + d.Want.String() + + if !tooWideForTypeErr(have, want) { + return []string{have, want} + } + + // Positions that only exist on one side, as is the case for arity errors, + // have nothing to be compared against, and are collapsed to their outermost + // type constructor. + haveArgs := util.Map(d.Have, elideType) + wantArgs := util.Map(d.Want.Args, elideType) + + for i := range min(len(haveArgs), len(wantArgs)) { + haveArgs[i], wantArgs[i] = diffArg(d.Have[i], d.Want.Args[i]) + } + + if d.Want.Variadic != nil { + wantArgs = append(wantArgs, elideType(d.Want.Variadic)+"...") + } + + return []string{ + "have: (" + strings.Join(haveArgs, ", ") + ")", + "want: (" + strings.Join(wantArgs, ", ") + ")", + } +} + +// diffArg renders an actual and an expected argument type side by side. The two +// are only diffed if they are actually in conflict: an argument that the +// function would have accepted is not what the error is about, and expanding it +// is what makes these messages unreadable in the first place. +func diffArg(have, want types.Type) (string, string) { + if have != nil && want != nil && unifies(unwrapNamedType(have), unwrapNamedType(want)) { + return elideType(have), elideType(want) + } + return sprintDiff(have, want) } func (d *ArgErrDetail) nilType() bool { @@ -1195,10 +1340,15 @@ func (a *UnificationErrDetail) nilType() bool { // Lines returns the string representation of the detail. func (a *UnificationErrDetail) Lines() []string { - lines := make([]string, 2) - lines[0] = fmt.Sprint("left : ", types.Sprint(a.Left)) - lines[1] = fmt.Sprint("right : ", types.Sprint(a.Right)) - return lines + leftLine := "left : " + types.Sprint(a.Left) + rightLine := "right : " + types.Sprint(a.Right) + + if !tooWideForTypeErr(leftLine, rightLine) { + return []string{leftLine, rightLine} + } + + left, right := sprintDiff(a.Left, a.Right) + return []string{"left : " + left, "right : " + right} } // RefErrUnsupportedDetail describes an undefined reference error where the @@ -1211,12 +1361,11 @@ type RefErrUnsupportedDetail struct { // Lines returns the string representation of the detail. func (r *RefErrUnsupportedDetail) Lines() []string { - lines := []string{ + return []string{ r.Ref.String(), strings.Repeat("^", len(r.Ref[:r.Pos+1].String())), fmt.Sprintf("have: %v", r.Have), } - return lines } // RefErrInvalidDetail describes an undefined reference error where the referenced @@ -1243,6 +1392,10 @@ func (r *RefErrInvalidDetail) Lines() []string { } if len(r.OneOf) > 0 { lines = append(lines, fmt.Sprintf("%swant (one of): %v", pad, r.OneOf)) + } else if r.Want == nil { + // Neither candidate keys nor a key type: the referenced value has no + // selectable keys at all (e.g. an empty object). + lines = append(lines, pad+"want (one of): []") } else { lines = append(lines, fmt.Sprintf("%swant (type): %v", pad, r.Want)) } @@ -1313,7 +1466,9 @@ func getOneOfForNode(node *typeTreeNode) []Value { func getOneOfForType(tpe types.Type) (result []Value) { switch tpe := tpe.(type) { case *types.Object: - for _, k := range tpe.Keys() { + keys := tpe.Keys() + result = slices.Grow(result, len(keys)) + for _, k := range keys { v, err := InterfaceToValue(k) if err != nil { panic(err) @@ -1325,6 +1480,7 @@ func getOneOfForType(tpe types.Type) (result []Value) { return getOneOfForType(tpe.Unwrap()) case types.Any: + result = slices.Grow(result, len(tpe)) for _, object := range tpe { objRes := getOneOfForType(object) result = append(result, objRes...) @@ -1419,7 +1575,7 @@ func override(ref Ref, t types.Type, o types.Type, rule *Rule) (types.Type, *Err } func getKeys(ref Ref, rule *Rule) ([]any, *Error) { - keys := []any{} + keys := make([]any, 0, len(ref)) for _, refElem := range ref { key, err := JSON(refElem.Value) if err != nil { @@ -1448,7 +1604,6 @@ func getObjectType(ref Ref, o types.Type, rule *Rule, d *types.DynamicProperty) } func getRuleAnnotation(as *AnnotationSet, rule *Rule) (result []*SchemaAnnotation) { - for _, x := range as.GetSubpackagesScope(rule.Module.Package.Path) { result = append(result, x.Schemas...) } @@ -1469,15 +1624,12 @@ func getRuleAnnotation(as *AnnotationSet, rule *Rule) (result []*SchemaAnnotatio } func processAnnotation(ss *SchemaSet, annot *SchemaAnnotation, rule *Rule, allowNet []string) (types.Type, *Error) { - var schema any - if annot.Schema != nil { if ss == nil { return nil, nil } - schema = ss.Get(annot.Schema) - if schema == nil { + if schema = ss.Get(annot.Schema); schema == nil { return nil, NewError(TypeErr, rule.Location, "undefined schema: %v", annot.Schema) } } else if annot.Definition != nil { diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/check_elide.go b/vendor/github.com/open-policy-agent/opa/v1/ast/check_elide.go new file mode 100644 index 0000000000..9f8ae05377 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/check_elide.go @@ -0,0 +1,399 @@ +// Copyright 2025 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "fmt" + "strings" + "unicode/utf8" + + "github.com/open-policy-agent/opa/v1/types" + "github.com/open-policy-agent/opa/v1/util" +) + +const ( + typeElision = "..." + + // maxTypeErrLineWidth is the width above which a type error detail line is + // re-rendered with nested type information elided. Details that already fit + // are left alone: eliding them would drop information without buying any + // readability. + maxTypeErrLineWidth = 80 + + // maxElidedTypeWidth is the width above which a type that is not itself the + // subject of the mismatch is collapsed to its outermost constructor. + maxElidedTypeWidth = 32 + + // maxTypeDiffDepth bounds the parallel walk so that deeply nested types + // cannot produce an unreadable message, or, for cyclic types, no message. + maxTypeDiffDepth = 8 +) + +// sprintDiff returns the string representations of a and b, keeping only the +// structure that is needed to see how the two differ. Sub-types that are equal +// on both sides are collapsed to their outermost type constructor, and object +// properties and any-members that are equal on both sides are dropped +// altogether. An ellipsis marks everything that was left out. +func sprintDiff(a, b types.Type) (string, string) { + return diffTypes(a, b, 0) +} + +// sprintElided returns the string representation of t with any type information +// nested more than depth levels below t replaced by an ellipsis. A depth of zero +// keeps the outermost type constructor only; a negative depth renders t in full, +// exactly as types.Sprint does. +func sprintElided(t types.Type, depth int) string { + if depth < 0 { + return types.Sprint(t) + } + + switch t := t.(type) { + case nil: + return types.Sprint(t) + case *types.NamedType: + // A name is not a level of nesting, so depth is passed through. + return t.Name + ": " + sprintElided(t.Type, depth) + case *types.Set: + if t.Of() == nil { + // The empty set has no element type to elide. + return t.String() + } + if depth == 0 { + return "set[" + typeElision + "]" + } + return "set[" + sprintElided(t.Of(), depth-1) + "]" + case *types.Array: + static := make([]string, 0, t.Len()) + if depth == 0 && t.Len() > 0 { + static = append(static, typeElision) + } else { + for i := range t.Len() { + static = append(static, sprintElided(t.Select(i), depth-1)) + } + } + var dynamic string + if dyn := t.Dynamic(); dyn != nil { + if depth == 0 { + dynamic = typeElision + } else { + dynamic = sprintElided(dyn, depth-1) + } + } + return sprintArray(static, dynamic) + case *types.Object: + props := t.StaticProperties() + static := make([]string, 0, len(props)) + if depth == 0 && len(props) > 0 { + static = append(static, typeElision) + } else { + for _, p := range props { + static = append(static, sprintProperty(p.Key, sprintElided(p.Value, depth-1))) + } + } + var dynamic string + if dyn := t.DynamicProperties(); dyn != nil { + if depth == 0 { + dynamic = typeElision + } else { + dynamic = sprintElided(dyn.Key, depth-1) + ": " + sprintElided(dyn.Value, depth-1) + } + } + return sprintObject(static, dynamic) + case types.Any: + if len(t) == 0 { + return t.String() + } + if depth == 0 { + return sprintAny([]string{typeElision}) + } + of := make([]string, len(t)) + for i := range t { + of[i] = sprintElided(t[i], depth-1) + } + return sprintAny(of) + case *types.Function: + args := t.FuncArgs() + params := make([]string, 0, len(args.Args)+1) + if depth == 0 { + if len(args.Args) > 0 || args.Variadic != nil { + params = append(params, typeElision) + } + if t.Result() == nil { + return sprintFunction(params, types.Sprint(nil)) + } + return sprintFunction(params, typeElision) + } + for i := range args.Args { + params = append(params, sprintElided(args.Args[i], depth-1)) + } + if args.Variadic != nil { + params = append(params, sprintElided(args.Variadic, depth-1)+"...") + } + return sprintFunction(params, sprintElided(t.Result(), depth-1)) + default: + // Scalars, and recursive types, which are rendered by name only. + return t.String() + } +} + +// elideType collapses t to its outermost type constructor, unless it is short +// enough that spelling it out costs nothing. +func elideType(t types.Type) string { + if full := types.Sprint(t); utf8.RuneCountInString(full) <= maxElidedTypeWidth { + return full + } + return sprintElided(t, 0) +} + +func unwrapNamedType(t types.Type) types.Type { + if n, ok := t.(*types.NamedType); ok { + return n.Type + } + return t +} + +// typesEqual reports whether a and b would render identically. Comparing the +// rendered form, rather than the type structure, keeps this safe for recursive +// types, which render as a name rather than as their unrolled definition. +func typesEqual(a, b types.Type) bool { + return types.Sprint(a) == types.Sprint(b) +} + +func diffTypes(a, b types.Type, depth int) (string, string) { + // A name is not a level of nesting, so depth is passed through. + if n, ok := a.(*types.NamedType); ok { + left, right := diffTypes(n.Type, b, depth) + return n.Name + ": " + left, right + } + if n, ok := b.(*types.NamedType); ok { + left, right := diffTypes(a, n.Type, depth) + return left, n.Name + ": " + right + } + + if depth >= maxTypeDiffDepth || a == nil || b == nil || typesEqual(a, b) { + return elideType(a), elideType(b) + } + + switch a := a.(type) { + case *types.Set: + if b, ok := b.(*types.Set); ok && a.Of() != nil && b.Of() != nil { + left, right := diffTypes(a.Of(), b.Of(), depth+1) + return "set[" + left + "]", "set[" + right + "]" + } + case *types.Array: + if b, ok := b.(*types.Array); ok { + return diffArrays(a, b, depth) + } + case *types.Object: + if b, ok := b.(*types.Object); ok { + return diffObjects(a, b, depth) + } + case types.Any: + if b, ok := b.(types.Any); ok { + return diffAnys(a, b) + } + case *types.Function: + if b, ok := b.(*types.Function); ok && a.Arity() == b.Arity() { + return diffFunctions(a, b, depth) + } + } + + // The outermost type constructors already differ, which is all the reader + // needs to see. + return elideType(a), elideType(b) +} + +func diffArrays(a, b *types.Array, depth int) (string, string) { + if a.Len() != b.Len() || (a.Dynamic() == nil) != (b.Dynamic() == nil) { + // Element types are still shown so that it is clear which of the two is + // the longer, or which one has a dynamic tail. + return sprintElided(a, 1), sprintElided(b, 1) + } + + // Array elements are positional, so equal ones are collapsed rather than + // dropped, keeping the two renderings aligned. + left := make([]string, 0, a.Len()) + right := make([]string, 0, b.Len()) + for i := range a.Len() { + l, r := diffTypes(a.Select(i), b.Select(i), depth+1) + left = append(left, l) + right = append(right, r) + } + + var dynLeft, dynRight string + if a.Dynamic() != nil { + dynLeft, dynRight = diffTypes(a.Dynamic(), b.Dynamic(), depth+1) + } + + return sprintArray(left, dynLeft), sprintArray(right, dynRight) +} + +func diffObjects(a, b *types.Object, depth int) (string, string) { + aProps, bProps := a.StaticProperties(), b.StaticProperties() + + var left, right []string + var elided bool + + // Static properties are sorted by key on both sides, so they can be walked in + // parallel. Those that are equal say nothing about why the two types were + // reported together, and are dropped. + i, j := 0, 0 + for i < len(aProps) || j < len(bProps) { + switch { + case j == len(bProps): + left = append(left, sprintProperty(aProps[i].Key, elideType(aProps[i].Value))) + i++ + case i == len(aProps): + right = append(right, sprintProperty(bProps[j].Key, elideType(bProps[j].Value))) + j++ + default: + switch util.Compare(aProps[i].Key, bProps[j].Key) { + case -1: + left = append(left, sprintProperty(aProps[i].Key, elideType(aProps[i].Value))) + i++ + case 1: + right = append(right, sprintProperty(bProps[j].Key, elideType(bProps[j].Value))) + j++ + default: + if typesEqual(aProps[i].Value, bProps[j].Value) { + elided = true + } else { + l, r := diffTypes(aProps[i].Value, bProps[j].Value, depth+1) + left = append(left, sprintProperty(aProps[i].Key, l)) + right = append(right, sprintProperty(bProps[j].Key, r)) + } + i++ + j++ + } + } + } + + aDyn, bDyn := a.DynamicProperties(), b.DynamicProperties() + var dynLeft, dynRight string + switch { + case aDyn != nil && bDyn != nil: + keyLeft, keyRight := diffTypes(aDyn.Key, bDyn.Key, depth+1) + valLeft, valRight := diffTypes(aDyn.Value, bDyn.Value, depth+1) + dynLeft, dynRight = keyLeft+": "+valLeft, keyRight+": "+valRight + case aDyn != nil: + dynLeft = elideType(aDyn.Key) + ": " + elideType(aDyn.Value) + case bDyn != nil: + dynRight = elideType(bDyn.Key) + ": " + elideType(bDyn.Value) + } + + return sprintObject(withElision(left, elided), dynLeft), sprintObject(withElision(right, elided), dynRight) +} + +func diffAnys(a, b types.Any) (string, string) { + // The members of an Any are unordered as far as the reader is concerned, so + // those that appear on both sides are dropped rather than collapsed. + left := make([]string, 0, len(a)) + right := make([]string, 0, len(b)) + var elided bool + + for _, tpe := range a { + if containsType(b, tpe) { + elided = true + } else { + left = append(left, elideType(tpe)) + } + } + for _, tpe := range b { + if !containsType(a, tpe) { + right = append(right, elideType(tpe)) + } + } + + return sprintAny(withElision(left, elided)), sprintAny(withElision(right, elided)) +} + +func diffFunctions(a, b *types.Function, depth int) (string, string) { + aArgs, bArgs := a.FuncArgs(), b.FuncArgs() + + left := make([]string, 0, len(aArgs.Args)+1) + right := make([]string, 0, len(bArgs.Args)+1) + for i := range aArgs.Args { + l, r := diffTypes(aArgs.Args[i], bArgs.Args[i], depth+1) + left = append(left, l) + right = append(right, r) + } + switch { + case aArgs.Variadic != nil && bArgs.Variadic != nil: + l, r := diffTypes(aArgs.Variadic, bArgs.Variadic, depth+1) + left = append(left, l+"...") + right = append(right, r+"...") + case aArgs.Variadic != nil: + left = append(left, elideType(aArgs.Variadic)+"...") + case bArgs.Variadic != nil: + right = append(right, elideType(bArgs.Variadic)+"...") + } + + resLeft, resRight := diffTypes(a.Result(), b.Result(), depth+1) + return sprintFunction(left, resLeft), sprintFunction(right, resRight) +} + +func containsType(haystack types.Any, needle types.Type) bool { + for _, tpe := range haystack { + if typesEqual(tpe, needle) { + return true + } + } + return false +} + +func withElision(parts []string, elided bool) []string { + if !elided { + return parts + } + return append(parts, typeElision) +} + +func sprintProperty(key any, value string) string { + return fmt.Sprintf("%v: %v", key, value) +} + +func sprintArray(static []string, dynamic string) string { + return sprintComposite("array", static, dynamic) +} + +func sprintObject(static []string, dynamic string) string { + return sprintComposite("object", static, dynamic) +} + +func sprintComposite(prefix string, static []string, dynamic string) string { + sb := strings.Builder{} + sb.WriteString(prefix) + if len(static) > 0 { + sb.WriteString("<") + sb.WriteString(strings.Join(static, ", ")) + sb.WriteString(">") + } + if dynamic != "" { + sb.WriteString("[") + sb.WriteString(dynamic) + sb.WriteString("]") + } + return sb.String() +} + +func sprintAny(of []string) string { + if len(of) == 0 { + return "any" + } + return "any<" + strings.Join(of, ", ") + ">" +} + +func sprintFunction(args []string, result string) string { + return "(" + strings.Join(args, ", ") + ") => " + result +} + +func tooWideForTypeErr(lines ...string) bool { + for _, line := range lines { + if utf8.RuneCountInString(line) > maxTypeErrLineWidth { + return true + } + } + return false +} diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/compare.go b/vendor/github.com/open-policy-agent/opa/v1/ast/compare.go index 81038e7a68..d3dde15743 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/compare.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/compare.go @@ -107,10 +107,9 @@ func Compare(a, b any) int { case Var: return VarCompare(a, b.(Var)) case Ref: - return termSliceCompare(a, b.(Ref)) + return slices.CompareFunc(a, b.(Ref), TermValueCompare) case *Array: - b := b.(*Array) - return termSliceCompare(a.elems, b.elems) + return slices.CompareFunc(a.elems, b.(*Array).elems, TermValueCompare) case *lazyObj: return Compare(a.force(), b) case *object: @@ -130,7 +129,7 @@ func Compare(a, b any) int { b := b.(*SetComprehension) return a.Compare(b) case Call: - return termSliceCompare(a, b.(Call)) + return slices.CompareFunc(a, b.(Call), TermValueCompare) case *Expr: return a.Compare(b.(*Expr)) case *SomeDecl: @@ -150,7 +149,7 @@ func Compare(a, b any) int { case *Rule: return a.Compare(b.(*Rule)) case Args: - return termSliceCompare(a, b.(Args)) + return slices.CompareFunc(a, b.(Args), TermValueCompare) case *Import: return a.Compare(b.(*Import)) case *Package: @@ -247,52 +246,6 @@ func sortOrder(x any) int { panic(fmt.Sprintf("illegal value: %T", x)) } -func rulesCompare(a, b []*Rule) int { - minLen := min(len(b), len(a)) - for i := range minLen { - if cmp := a[i].Compare(b[i]); cmp != 0 { - return cmp - } - } - if len(a) < len(b) { - return -1 - } - if len(b) < len(a) { - return 1 - } - return 0 -} - -func termSliceCompare(a, b []*Term) int { - minLen := min(len(b), len(a)) - for i := range minLen { - if cmp := a[i].Value.Compare(b[i].Value); cmp != 0 { - return cmp - } - } - if len(a) < len(b) { - return -1 - } else if len(b) < len(a) { - return 1 - } - return 0 -} - -func withSliceCompare(a, b []*With) int { - minLen := min(len(b), len(a)) - for i := range minLen { - if cmp := a[i].Compare(b[i]); cmp != 0 { - return cmp - } - } - if len(a) < len(b) { - return -1 - } else if len(b) < len(a) { - return 1 - } - return 0 -} - func VarCompare(a, b Var) int { if a == b { return 0 @@ -304,6 +257,9 @@ func VarCompare(a, b Var) int { } func TermValueCompare(a, b *Term) int { + if a == b { + return 0 + } return a.Value.Compare(b.Value) } @@ -329,7 +285,7 @@ func ValueEqual(a, b Value) bool { } func RefCompare(a, b Ref) int { - return termSliceCompare(a, b) + return slices.CompareFunc(a, b, TermValueCompare) } func RefEqual(a, b Ref) bool { diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/compile.go b/vendor/github.com/open-policy-agent/opa/v1/ast/compile.go index f40835ed74..dae5320889 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/compile.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/compile.go @@ -22,13 +22,31 @@ import ( "github.com/open-policy-agent/opa/v1/util" ) -// CompileErrorLimitDefault is the default number errors a compiler will allow before -// exiting. -const CompileErrorLimitDefault = 10 +const ( + // CompileErrorLimitDefault is the default number + // of errors a compiler will allow before exiting. + CompileErrorLimitDefault = 10 + LocalVarPrefix = "__local" + + errAssignInNegated = "cannot assign vars inside negated expression" + errAssignInAndOperand = "cannot assign vars inside implicit and operand" + errAssignInOrOperand = "cannot assign vars inside implicit or operand" +) var ( - errLimitReached = newErrorString(CompileErr, nil, "error limit reached") - emptyPackage = &Package{Path: Ref{VarTerm("")}} + // SafetyCheckVisitorParams defines the AST visitor parameters to use for collecting + // variables during the safety check. This has to be exported because it's relied on + // by the copy propagation implementation in topdown. + SafetyCheckVisitorParams = VarVisitorParams{SkipRefCallHead: true, SkipClosures: true} + // TODO(tsandall): Improve this so that users can either supply this list explicitly + // or the information is maintained on the built-in function declaration. What we really + // need to know is whether the built-in function allows callers to push down output + // values or not. It's unlikely that anything outside of OPA does this today so this + // solution is fine for now. + comprehensionIndexDenylist = map[string]int{WalkBuiltin.Name: len(WalkBuiltin.Decl.FuncArgs().Args)} + errLimitReached = newErrorString(CompileErr, nil, "error limit reached") + emptyPackage = &Package{Path: Ref{VarTerm("")}} + futureKeywordsPrefix = Ref{FutureRootDocument, InternedTerm("keywords")} ) // Compiler contains the state of a compilation process. @@ -161,6 +179,8 @@ type Compiler struct { defaultRegoVersion RegoVersion skipStages map[StageID]struct{} // stages to skip during compilation plan *executionPlan // computed execution plan (cached) + unusedImports []*Import // imports found unused during ref resolution, reported by CheckUnusedImports + unrecoverableErr bool // at least one recorded error prevents the remaining stages from running } func (c *Compiler) DefaultRegoVersion() RegoVersion { @@ -179,6 +199,7 @@ type StageID string // at least lets you know what your attention is needed when you depend on the stages. const ( StageResolveRefs StageID = "ResolveRefs" + StageCheckUnusedImports StageID = "CheckUnusedImports" StageInitLocalVarGen StageID = "InitLocalVarGen" StageRewriteRuleHeadRefs StageID = "RewriteRuleHeadRefs" StageCheckKeywordOverrides StageID = "CheckKeywordOverrides" @@ -221,6 +242,7 @@ const ( func AllStages() []StageID { return []StageID{ StageResolveRefs, + StageCheckUnusedImports, StageInitLocalVarGen, StageRewriteRuleHeadRefs, StageCheckKeywordOverrides, @@ -260,7 +282,7 @@ func AllStages() []StageID { // CompilerEvalMode allows toggling certain stages that are only // needed for certain modes, Concretely, only "topdown" mode will // have the compiler build comprehension and rule indices. -type CompilerEvalMode int +type CompilerEvalMode uint8 const ( // EvalModeTopdown (default) instructs the compiler to build rule @@ -376,7 +398,7 @@ type QueryCompiler interface { // WithStageAfter registers a stage to run during query compilation after // the named stage. // - // Caution: Use [ast.QueryCompiler.WithStageAfterID] instead. It provides + // Caution: Use [QueryCompiler.WithStageAfterID] instead. It provides // more (Golang) compile-time safety WithStageAfter(after string, stage QueryCompilerStageDefinition) QueryCompiler @@ -439,6 +461,7 @@ func NewCompiler() *Compiler { // load additional modules. If any stages run before resolution, they // need to be re-run after resolution. {StageResolveRefs, "compile_stage_resolve_refs", c.resolveAllRefs}, + {StageCheckUnusedImports, "compile_stage_check_unused_imports", c.checkUnusedImports}, // The local variable generator must be initialized after references are // resolved and the dynamic module loader has run but before subsequent // stages that need to generate variables. @@ -515,7 +538,7 @@ func (c *Compiler) WithPathConflictsCheckRoots(rootPaths []string) *Compiler { // WithStageAfter registers a stage to run during compilation after // the named stage. // -// Caution: Consider using [ast.QueryCompiler.WithStageAfterID] instead. It provides +// Caution: Consider using [Compiler.WithStageAfterID] instead. It provides // more (Golang) compile-time safety func (c *Compiler) WithStageAfter(after string, stage CompilerStageDefinition) *Compiler { c.after[after] = append(c.after[after], stage) @@ -836,15 +859,12 @@ func (c *Compiler) GetRulesWithPrefix(ref Ref) (rules []*Rule) { // GetRules("data.a.b.c") => [rule1, rule2] // GetRules("data.a.b.d") => nil func (c *Compiler) GetRules(ref Ref) []*Rule { - set := map[*Rule]struct{}{} + virt := c.GetRulesForVirtualDocument(ref) + pref := c.GetRulesWithPrefix(ref) - for _, rule := range c.GetRulesForVirtualDocument(ref) { - set[rule] = struct{}{} - } - - for _, rule := range c.GetRulesWithPrefix(ref) { - set[rule] = struct{}{} - } + set := make(map[*Rule]struct{}, len(virt)+len(pref)) + insertRules(set, virt) + insertRules(set, pref) return util.Keys(set) } @@ -918,7 +938,7 @@ func (c *Compiler) GetRulesDynamicWithOpts(ref Ref, opts RulesOptions) []*Rule { if child := node.Child(ref[i].Value); child != nil { if len(child.Values) > 0 { // Add any rules at this position - insertRules(set, child.Values) + insertRulesIntersecting(set, child.Values, ref, i+1) } // There might still be "sub-rules" contributing key-value "overrides" for e.g. partial object rules, continue walking walk(child, i+1) @@ -933,7 +953,7 @@ func (c *Compiler) GetRulesDynamicWithOpts(ref Ref, opts RulesOptions) []*Rule { if child.Hide && !opts.IncludeHiddenModules { continue } - insertRules(set, child.Values) + insertRulesIntersecting(set, child.Values, ref, i+1) walk(child, i+1) } } @@ -950,6 +970,31 @@ func insertRules(set map[*Rule]struct{}, rules []*Rule) { } } +// insertRulesIntersecting adds the rules whose refs could still intersect ref +// beyond position i. Rules with general refs are all stored at the ground +// prefix of their ref, so a rule like data.a.p[x].foo.bar sits at data.a.p +// alongside data.a.p[x].foo.baz. Without comparing the remaining parts, a ref +// to one of them would appear to refer to both. +func insertRulesIntersecting(set map[*Rule]struct{}, rules []*Rule, ref Ref, i int) { + for _, rule := range rules { + if refsMayIntersect(rule.Ref(), ref, i) { + set[rule] = struct{}{} + } + } +} + +// refsMayIntersect compares a and b from position i onwards, treating parts +// that aren't statically known as matching anything. +func refsMayIntersect(a, b Ref, i int) bool { + for ; i < len(a) && i < len(b); i++ { + x, y := a[i].Value, b[i].Value + if IsConstant(x) && IsConstant(y) && x.Compare(y) != 0 { + return false + } + } + return true +} + // RuleIndex returns a RuleIndex built for the rule set referred to by path. // The path must refer to the rule set exactly, i.e., given a rule set at path // data.a.b.c.p, refs data.a.b.c.p.x and data.a.b.c would not return a @@ -971,11 +1016,7 @@ func (c *Compiler) PassesTypeCheck(body Body) bool { // PassesTypeCheckRules determines whether the given rules passes type checking func (c *Compiler) PassesTypeCheckRules(rules []*Rule) Errors { - elems := make([]util.T, 0, len(rules)) - - for _, rule := range rules { - elems = append(elems, rule) - } + elems := util.ToSliceOf[util.T](rules) // Load the global input schema if one was provided. if c.schemaSet != nil { @@ -1145,12 +1186,13 @@ func (c *Compiler) buildRuleIndices() { // b.c[x].e := 1 { x := input.x } // b.c.d := 2 // b.c.d2.e[x] := 3 { x := input.x } - for _, child := range node.Children { - child.DepthFirst(func(c *TreeNode) bool { - rules = append(rules, c.Values...) - return false - }) - } + // Cleared rather than truncated: rules aliases node.Values, which the + // walk hands back along with everything below it. + rules = nil + node.DepthFirst(func(c *TreeNode) bool { + rules = append(rules, c.Values...) + return false + }) } index := newBaseDocEqIndex(c.isVirtual) @@ -1180,8 +1222,6 @@ func (c *Compiler) buildComprehensionIndices() { varVisitorPool.Put(vis) } -var futureKeywordsPrefix = Ref{FutureRootDocument, InternedTerm("keywords")} - // buildRequiredCapabilities updates the required capabilities on the compiler // to include any keyword and feature dependencies present in the modules. The // built-in function dependencies will have already been added by the type @@ -1296,18 +1336,19 @@ func (c *Compiler) checkRecursion() { func (c *Compiler) checkSelfPath(loc *Location, eq func(a, b util.T) bool, a, b util.T) { tr := NewGraphTraversal(c.Graph) if p := util.DFSPath(tr, eq, a, b); len(p) > 0 { + rw := rewriteVarsInRef(c.RewrittenVars) n := make([]string, 0, len(p)) for _, x := range p { - n = append(n, astNodeToString(x)) + n = append(n, astNodeToString(rw, x)) } - if !c.err(NewError(RecursionErr, loc, "rule %v is recursive: %v", astNodeToString(a), strings.Join(n, " -> "))) { + if !c.err(NewError(RecursionErr, loc, "rule %v is recursive: %v", astNodeToString(rw, a), strings.Join(n, " -> "))) { return } } } -func astNodeToString(x any) string { - return x.(*Rule).Ref().String() +func astNodeToString(rw varRewriter, x any) string { + return rw(x.(*Rule).Ref().CopyNonGround()).String() // varRewriter operates in-place } // checkRuleConflicts ensures that rules definitions are not in conflict. @@ -1515,10 +1556,10 @@ func checkUndefinedFuncs(env *TypeEnv, x any, arity func(Ref) int, rwVars map[Va } func arityMismatchError(env *TypeEnv, f Ref, expr *Expr, exp, act int) *Error { - if want, ok := env.Get(f).(*types.Function); ok { // generate richer error for built-in functions + if want, ok := env.GetByRef(f).(*types.Function); ok { // generate richer error for built-in functions have := make([]types.Type, len(expr.Operands())) for i, op := range expr.Operands() { - have[i] = env.Get(op) + have[i] = env.GetByValue(op.Value) } return newArgError(expr.Loc(), f, "arity mismatch", have, want.NamedFuncArgs()) } @@ -1535,18 +1576,19 @@ func (c *Compiler) checkSafetyRuleBodies() { vis := varVisitorPool.Get() for _, name := range c.sorted { - m := c.Modules[name] - scopes := ruleScopes{module: m} - WalkRules(m, func(r *Rule) bool { - vis = vis.Clear() - // vis.vars == safe - vis.vars.Update(ReservedVars) - if len(r.Head.Args) > 0 { - vis.WalkArgs(r.Head.Args) - } - r.Body = c.checkBodySafety(vis.vars, r.Body, r, &scopes) - return false - }) + scopes := ruleScopes{module: c.Modules[name]} + for _, rule := range c.Modules[name].Rules { + WalkRules(rule, func(r *Rule) bool { + vis = vis.Clear() + // vis.vars == safe + vis.vars.Update(ReservedVars) + if len(r.Head.Args) > 0 { + vis.WalkArgs(r.Head.Args) + } + r.Body = c.checkBodySafety(vis.vars, r.Body, r, &scopes) + return false + }) + } } varVisitorPool.Put(vis) @@ -1564,15 +1606,6 @@ func (c *Compiler) checkBodySafety(safe VarSet, b Body, r *Rule, scopes *ruleSco return reordered } -// SafetyCheckVisitorParams defines the AST visitor parameters to use for collecting -// variables during the safety check. This has to be exported because it's relied on -// by the copy propagation implementation in topdown. -// TODO: deprecate? -var SafetyCheckVisitorParams = VarVisitorParams{ - SkipRefCallHead: true, - SkipClosures: true, -} - // checkSafetyRuleHeads ensures that variables appearing in the head of a // rule also appear in the body. func (c *Compiler) checkSafetyRuleHeads() { @@ -1901,9 +1934,7 @@ func (c *Compiler) checkTypes() { as = c.annotationSet } env, errs := checker.CheckTypes(c.TypeEnv, sorted, as) - for _, err := range errs { - c.err(err) - } + c.errRecoverable(errs...) c.TypeEnv = env } @@ -1947,10 +1978,7 @@ func (c *Compiler) checkUnsafeBuiltins() { } for _, name := range c.sorted { - errs := checkUnsafeBuiltins(c.unsafeBuiltinsMap, c.Modules[name]) - for _, err := range errs { - c.err(err) - } + c.errRecoverable(checkUnsafeBuiltins(c.unsafeBuiltinsMap, c.Modules[name])...) } } @@ -1968,7 +1996,7 @@ func (c *Compiler) checkDeprecatedBuiltins() { for _, name := range c.sorted { if c.strict || c.Modules[name].regoV1Compatible() { - c.err(checkDeprecatedBuiltins(c.deprecatedBuiltinsMap, c.Modules[name])...) + c.errRecoverable(checkDeprecatedBuiltins(c.deprecatedBuiltinsMap, c.Modules[name])...) } } } @@ -1976,25 +2004,40 @@ func (c *Compiler) checkDeprecatedBuiltins() { func (c *Compiler) compile() { plan := c.getOrBuildPlan() + defer c.sortErrors() + if c.metrics != nil { for _, s := range plan.stages { c.metrics.Timer(s.metricName).Start() s.f() c.metrics.Timer(s.metricName).Stop() - if c.Failed() { + if c.unrecoverableErr { return } } } else { for _, s := range plan.stages { s.f() - if c.Failed() { + if c.unrecoverableErr { return } } } } +// sortErrors orders errors by location so reports read top-to-bottom. The error +// limit marker has no location of its own and is swapped to the end to keep it +// last, wherever it was recorded. +func (c *Compiler) sortErrors() { + errs := c.Errors + if i := slices.Index(errs, errLimitReached); i >= 0 { + errs[i], errs[len(errs)-1] = errs[len(errs)-1], errs[i] + errs = errs[:len(errs)-1] + } + + errs.Sort() +} + func (c *Compiler) init() { if c.initialized { @@ -2071,7 +2114,19 @@ func (c *Compiler) init() { c.initialized = true } +// err records an error that stops compilation after the current stage. func (c *Compiler) err(errs ...*Error) bool { // returns if we should continue + return c.recordErrs(false, errs...) +} + +// errRecoverable records an error that lets the remaining stages run, so that one +// compilation can report every violation it finds. Only for checks that leave the +// modules in a state later stages can't produce bogus follow-up errors from. +func (c *Compiler) errRecoverable(errs ...*Error) bool { + return c.recordErrs(true, errs...) +} + +func (c *Compiler) recordErrs(recoverable bool, errs ...*Error) bool { if len(errs) == 0 { return true } @@ -2081,6 +2136,7 @@ func (c *Compiler) err(errs ...*Error) bool { // returns if we should continue if c.maxErrs <= 0 { c.Errors = append(c.Errors, errs...) + c.unrecoverableErr = c.unrecoverableErr || !recoverable return true } @@ -2099,6 +2155,8 @@ func (c *Compiler) err(errs ...*Error) bool { // returns if we should continue c.errCount += uint32(numToTake) c.Errors = append(c.Errors, errs[:numToTake]...) + // Nothing left to collect once the limit is hit, so stop there too. + c.unrecoverableErr = c.unrecoverableErr || !recoverable || isLimitReachedInThisCall if isLimitReachedInThisCall { c.Errors = append(c.Errors, errLimitReached) } @@ -2106,42 +2164,35 @@ func (c *Compiler) err(errs ...*Error) bool { // returns if we should continue return !isLimitReachedInThisCall // Return false if the limit was reached, true otherwise. } -func (c *Compiler) getExports() *util.HasherMap[Ref, []Ref] { - rules := util.NewHasherMap[Ref, []Ref](RefEqual) - +func (c *Compiler) getExport(pkg Ref) []Ref { + var refs []Ref for _, name := range c.sorted { - for _, rule := range c.Modules[name].Rules { - hashMapAdd(rules, c.Modules[name].Package.Path, rule.Head.Ref().GroundPrefix()) + if RefEqual(pkg, c.Modules[name].Package.Path) { + refs = slices.Grow(refs, len(c.Modules[name].Rules)) + for _, rule := range c.Modules[name].Rules { + refs = append(refs, rule.Head.Ref().GroundPrefix()) + } } } - - return rules + return refs } -func refSliceEqual(a, b []Ref) bool { - if len(a) != len(b) { - return false - } - for i := range a { - if !a[i].Equal(b[i]) { - return false - } - } - return true -} +// getExports groups every module's exported rule refs by package path in one +// pass. Use this instead of getExport per package, which is quadratic. +func (c *Compiler) getExports() *util.HasherMap[Ref, []Ref] { + rules := util.NewHasherMap[Ref, []Ref](RefEqual) -func hashMapAdd(rules *util.HasherMap[Ref, []Ref], pkg, rule Ref) { - prev, ok := rules.Get(pkg) - if !ok { - rules.Put(pkg, []Ref{rule}) - return - } - for _, p := range prev { - if p.Equal(rule) { - return + for _, name := range c.sorted { + mod := c.Modules[name] + refs, _ := rules.Get(mod.Package.Path) + refs = slices.Grow(refs, len(mod.Rules)) + for _, rule := range mod.Rules { + refs = append(refs, rule.Head.Ref().GroundPrefix()) } + rules.Put(mod.Package.Path, refs) } - rules.Put(pkg, append(prev, rule)) + + return rules } func (c *Compiler) GetAnnotationSet() *AnnotationSet { @@ -2157,7 +2208,7 @@ func (c *Compiler) checkImports() { for _, name := range c.sorted { for _, imp := range c.Modules[name].Imports { if !supportsRegoV1Import && RegoV1CompatibleRef.Equal(imp.Path.Value) { - if !c.err(NewError(CompileErr, imp.Loc(), "rego.v1 import is not supported")) { + if !c.errRecoverable(NewError(CompileErr, imp.Loc(), "rego.v1 import is not supported")) { continue } } @@ -2168,13 +2219,25 @@ func (c *Compiler) checkImports() { } } - c.err(checkDuplicateImports(modules)...) + c.errRecoverable(checkDuplicateImports(modules)...) +} + +// checkUnusedImports reports the imports resolveAllRefs found unused. Strict mode +// only, and a stage of its own so these don't cut compilation short. +func (c *Compiler) checkUnusedImports() { + for _, imp := range c.unusedImports { + if !c.errRecoverable(NewError(CompileErr, imp.Location, "%s unused", imp.String())) { + break + } + } + + c.unusedImports = nil } func (c *Compiler) checkKeywordOverrides() { for _, name := range c.sorted { if c.strict || c.moduleIsRegoV1Compatible(c.Modules[name]) { - if !c.err(checkRootDocumentOverrides(c.Modules[name])...) { + if !c.errRecoverable(checkRootDocumentOverrides(c.Modules[name])...) { continue } } @@ -2227,15 +2290,12 @@ func (c *Compiler) moduleIsRegoV1Compatible(mod *Module) bool { // // The reference "c.d.e" would be resolved to "data.a.b.c.d.e". func (c *Compiler) resolveAllRefs() { - rules := c.getExports() + exports := c.getExports() + c.unusedImports = nil for _, name := range c.sorted { mod := c.Modules[name] - var ruleExports []Ref - if x, ok := rules.Get(mod.Package.Path); ok { - ruleExports = x - } - + ruleExports, _ := exports.Get(mod.Package.Path) globals := getGlobals(mod.Package, ruleExports, mod.Imports) WalkRules(mod, func(rule *Rule) bool { @@ -2246,7 +2306,7 @@ func (c *Compiler) resolveAllRefs() { return false }) - if c.strict { // check for unused imports + if c.strict { // collect unused imports, reported by the CheckUnusedImports stage for _, imp := range mod.Imports { path := imp.Path.Value.(Ref) if FutureRootDocument.Equal(path[0]) || RegoRootDocument.Equal(path[0]) { @@ -2255,9 +2315,7 @@ func (c *Compiler) resolveAllRefs() { for v, u := range globals { if v == imp.Name() && !u.used { - if !c.err(NewError(CompileErr, imp.Location, "%s unused", imp.String())) { - return - } + c.unusedImports = append(c.unusedImports, imp) } } } @@ -2303,10 +2361,23 @@ func (c *Compiler) initLocalVarGen() { func (c *Compiler) rewriteComprehensionTerms() { f := newEqualityFactory(c.localvargen) for _, name := range c.sorted { + // Transform rebuilds what it walks, so finding nothing is not free. + if !ContainsComprehensions(c.Modules[name]) { + continue + } _, _ = rewriteComprehensionTerms(f, c.Modules[name]) // ignore error } } +func containsWith(x any) bool { + found := false + WalkWiths(x, func(*With) bool { + found = true + return found + }) + return found +} + func (c *Compiler) rewriteExprTerms() { for _, name := range c.sorted { WalkRules(c.Modules[name], func(rule *Rule) bool { @@ -2378,6 +2449,8 @@ func (c *Compiler) rewriteRuleHeadRefs() { } } +// checkVoidCalls errors are not recoverable: the type checker has no type for an +// expression using a void result, and reports a bogus "undefined function" for it. func (c *Compiler) checkVoidCalls() { for _, name := range c.sorted { c.err(checkVoidCalls(c.TypeEnv, c.Modules[name])...) @@ -2468,6 +2541,12 @@ func (c *Compiler) rewriteTemplateStrings() { for _, name := range c.sorted { mod := c.Modules[name] WalkRules(mod, func(r *Rule) bool { + // The output vars computed below are read only to resolve a template + // string, and most rules have none to resolve. + if !containsTemplateString(r) { + return false + } + tsr = tsr.Clear() safe := r.Head.Args.Vars() @@ -2497,6 +2576,17 @@ func (c *Compiler) rewriteTemplateStrings() { } } +func containsTemplateString(x any) bool { + found := false + WalkTerms(x, func(t *Term) bool { + if _, ok := t.Value.(*TemplateString); ok { + found = true + } + return found + }) + return found +} + func rewriteTemplateStrings(tsr *templateStringRewriter, globals VarSet, x any) (bool, VarSet, Errors) { var errs Errors var modified bool @@ -2510,7 +2600,7 @@ func rewriteTemplateStrings(tsr *templateStringRewriter, globals VarSet, x any) safe = globals.Copy() } - vis := &GenericVisitor{func(x any) bool { + vis := NewGenericVisitor(func(x any) bool { var modrec bool var errsrec Errors switch x := x.(type) { @@ -2567,7 +2657,7 @@ func rewriteTemplateStrings(tsr *templateStringRewriter, globals VarSet, x any) } errs = append(errs, errsrec...) return false - }} + }) vis.Walk(x) return modified, safe, errs @@ -2723,7 +2813,7 @@ func checkVoidCalls(env *TypeEnv, x any) Errors { var errs Errors WalkTerms(x, func(x *Term) bool { if call, ok := x.Value.(Call); ok { - if tpe, ok := env.Get(call[0]).(*types.Function); ok && tpe.Result() == nil { + if tpe, ok := env.GetByValue(call[0].Value).(*types.Function); ok && tpe.Result() == nil { errs = append(errs, NewError(TypeErr, x.Loc(), "%v used as value", call)) } } @@ -2876,7 +2966,7 @@ func containsNestedBody(x any) bool { func erasePrintCalls(node any) bool { var modified bool - NewGenericVisitor(func(x any) bool { + vis := NewGenericVisitor(func(x any) bool { var modrec bool switch x := x.(type) { case *Rule: @@ -2906,7 +2996,8 @@ func erasePrintCalls(node any) bool { modified = true } return false - }).Walk(node) + }) + vis.Walk(node) return modified } @@ -3258,7 +3349,7 @@ func createMetadataChain(chain []*AnnotationsRef) (*Term, *Error) { func (c *Compiler) rewriteLocalVars() { var assignment bool - args := NewVarVisitor() + args := varVisitorPool.Get() argsStack := newLocalDeclaredVars() for _, name := range c.sorted { @@ -3284,6 +3375,9 @@ func (c *Compiler) rewriteLocalVars() { if !c.err(errs...) { return true } + if !c.errRecoverable(stack.unused...) { + return true + } if stack.assignment { assignment = true } @@ -3299,7 +3393,13 @@ func (c *Compiler) rewriteLocalVars() { // Report an error for each unused function argument for arg := range unusedArgs { if !arg.IsWildcard() { - if !c.err(NewError(CompileErr, rule.Head.Location, "unused argument %v. (hint: use _ (wildcard variable) instead)", arg)) { + err := NewError( + CompileErr, + rule.Head.Location, + "unused argument %v. (hint: use _ (wildcard variable) instead)", + arg, + ) + if !c.errRecoverable(err) { return true } } @@ -3313,6 +3413,8 @@ func (c *Compiler) rewriteLocalVars() { if assignment { c.Required.addBuiltinSorted(Assign) } + + varVisitorPool.Put(args) } func (c *Compiler) rewriteLocalVarsInRule(rule *Rule, unusedArgs VarSet, argsStack *localDeclaredVars, gen *localVarGenerator) (*localDeclaredVars, Errors) { @@ -3337,11 +3439,14 @@ func (c *Compiler) rewriteLocalVarsInRule(rule *Rule, unusedArgs VarSet, argsSta strict: c.strict, } - NewGenericVisitor(nestedXform.Visit).Walk(rule.Head) + nxfVis := NewGenericVisitor(nestedXform.Visit) + nxfVis.Walk(rule.Head) c.err(nestedXform.errs...) // NB(sr): This is a bit bogus -- Why not return them? + c.errRecoverable(nestedXform.unused...) // Rewrite assignments in body. - vis := NewVarVisitor() + vis := varVisitorPool.Get() + defer varVisitorPool.Put(vis) for _, t := range rule.Head.Ref()[1:] { if !IsScalar(t.Value) { @@ -3378,31 +3483,32 @@ func (c *Compiler) rewriteLocalVarsInRule(rule *Rule, unusedArgs VarSet, argsSta // references (stay unsafe-var errors), call operators (SkipRefCallHead), // and `with` targets/values (possible function mocks). if len(c.builtins) > 0 { - bodyVis := NewVarVisitor().WithParams(VarVisitorParams{ + bodyVis := varVisitorPool.Get().WithParams(VarVisitorParams{ SkipRefCallHead: true, SkipClosures: true, }) + defer varVisitorPool.Put(bodyVis) + bodyVis.Walk(rule.Body) bodyVars := bodyVis.Vars() - declaredInBody := declaredVars(rule.Body) + declaredInBody := declaredBodyVars(rule.Body) - withVars := NewVarSet() - NewGenericVisitor(func(x any) bool { - if w, ok := x.(*With); ok { - WalkVars(w, func(v Var) bool { - withVars.Add(v) - return false - }) - } + withVis := varVisitorPool.Get().WithParams(VarVisitorParams{SkipRefCallHead: true}) + defer varVisitorPool.Put(withVis) + + f := func(w *With) bool { + withVis.Walk(w.Target.Value) + withVis.Walk(w.Value.Value) return false - }).Walk(rule) + } + WalkWiths(rule, f) for _, v := range bodyVars.Sorted() { if _, ok := c.builtins[v.String()]; !ok { continue } - if declaredInBody.Contains(v) || withVars.Contains(v) { + if declaredInBody.Contains(v) || withVis.Vars().Contains(v) { continue } if _, ok := stack.Declared(v); ok { @@ -3471,35 +3577,42 @@ func headMayHaveVars(head *Head) bool { type rewriteNestedHeadVarLocalTransform struct { gen *localVarGenerator errs Errors + unused Errors // strict-mode "unused var" diagnostics, see localDeclaredVars.unused RewrittenVars map[Var]Var strict bool } func (xform *rewriteNestedHeadVarLocalTransform) Visit(x any) bool { if term, ok := x.(*Term); ok { + if IsScalar(term.Value) { + return false + } + stop := false stack := newLocalDeclaredVars() switch x := term.Value.(type) { case *object: - vis := NewGenericVisitor(xform.Visit) - cpy, _ := x.Map(func(k, v *Term) (*Term, *Term, error) { - kcpy := k.Copy() - vis.Walk(kcpy) - vcpy := v.Copy() - vis.Walk(vcpy) - return kcpy, vcpy, nil - }) - term.Value = cpy + if !x.IsGround() { + vis := NewGenericVisitor(xform.Visit) + term.Value, _ = x.Map(func(k, v *Term) (*Term, *Term, error) { + kcpy := k.Copy() + vis.Walk(kcpy) + vcpy := v.Copy() + vis.Walk(vcpy) + return kcpy, vcpy, nil + }) + } stop = true case *set: - vis := NewGenericVisitor(xform.Visit) - cpy, _ := x.Map(func(v *Term) (*Term, error) { - vcpy := v.Copy() - vis.Walk(vcpy) - return vcpy, nil - }) - term.Value = cpy + if !x.IsGround() { + vis := NewGenericVisitor(xform.Visit) + term.Value, _ = x.Map(func(v *Term) (*Term, error) { + vcpy := v.Copy() + vis.Walk(vcpy) + return vcpy, nil + }) + } stop = true case *ArrayComprehension: xform.errs = rewriteDeclaredVarsInArrayComprehension(xform.gen, stack, x, xform.errs, xform.strict) @@ -3516,6 +3629,7 @@ func (xform *rewriteNestedHeadVarLocalTransform) Visit(x any) bool { } maps.Copy(xform.RewrittenVars, stack.rewritten) + xform.unused = append(xform.unused, stack.unused...) return stop } @@ -3537,14 +3651,9 @@ func (xform rewriteHeadVarLocalTransform) Transform(x any) (any, error) { } func (c *Compiler) rewriteLocalArgVars(gen *localVarGenerator, stack *localDeclaredVars, rule *Rule) { - vis := &ruleArgLocalRewriter{ - stack: stack, - gen: gen, - } - - for i := range rule.Head.Args { - Walk(vis, rule.Head.Args[i]) - } + vis := &ruleArgLocalRewriter{stack: stack, gen: gen} + vis.gv = &GenericVisitor{f: vis.visit} + vis.gv.Walk(rule.Head.Args) c.err(vis.errs...) } @@ -3552,16 +3661,15 @@ func (c *Compiler) rewriteLocalArgVars(gen *localVarGenerator, stack *localDecla type ruleArgLocalRewriter struct { stack *localDeclaredVars gen *localVarGenerator + gv *GenericVisitor errs []*Error } -func (vis *ruleArgLocalRewriter) Visit(x any) Visitor { - - t, ok := x.(*Term) +func (vis *ruleArgLocalRewriter) visit(a any) bool { + t, ok := a.(*Term) if !ok { - return vis + return false } - switch v := t.Value.(type) { case Var: gv, ok := vis.stack.Declared(v) @@ -3572,37 +3680,39 @@ func (vis *ruleArgLocalRewriter) Visit(x any) Visitor { vis.stack.Insert(v, gv, argVar) } t.Value = gv - return nil + return true case *object: if cpy, err := v.Map(func(k, v *Term) (*Term, *Term, error) { vcpy := v.Copy() - Walk(vis, vcpy) + vis.gv.Walk(vcpy) return k, vcpy, nil }); err != nil { vis.errs = append(vis.errs, newErrorString(CompileErr, t.Location, err.Error())) } else { t.Value = cpy } - return nil + return true case Null, Boolean, Number, String, *ArrayComprehension, *SetComprehension, *ObjectComprehension, Set, *TemplateString: // Scalars are no-ops. Comprehensions and template-strings are handled above. Sets must not // contain variables. - return nil + return true case Call: vis.errs = append(vis.errs, NewError(CompileErr, t.Location, "rule arguments cannot contain calls")) - return nil - default: - // Recurse on refs and arrays. Any embedded - // variables can be rewritten. - return vis + return true } + // Recurse on refs and arrays. Any embedded variables can be rewritten. + return false } func (c *Compiler) rewriteWithModifiers() { f := newEqualityFactory(c.localvargen) for _, name := range c.sorted { mod := c.Modules[name] - t := NewGenericTransformer(func(x any) (any, error) { + // As above: a module with no with modifier would be rebuilt unchanged. + if !containsWith(mod) { + continue + } + t := GenericTransformer{f: func(x any) (any, error) { body, ok := x.(Body) if !ok { return x, nil @@ -3613,7 +3723,7 @@ func (c *Compiler) rewriteWithModifiers() { } return body, nil - }) + }} _, _ = Transform(t, mod) // ignore error } } @@ -3810,18 +3920,13 @@ func (qc *queryCompiler) resolveRefs(qctx *QueryContext, body Body) (Body, error pkg = emptyPackage } if pkg != nil { - var ruleExports []Ref - rules := qc.compiler.getExports() - if exist, ok := rules.Get(pkg.Path); ok { - ruleExports = exist - } - + ruleExports := qc.compiler.getExport(pkg.Path) globals = getGlobals(qctx.Package, ruleExports, qctx.Imports) qctx.Imports = nil } } - ignore := &declaredVarStack{declaredVars(body)} + ignore := &declaredVarStack{declaredBodyVars(body)} return resolveRefsInBody(globals, ignore, body), nil } @@ -3855,6 +3960,7 @@ func (qc *queryCompiler) rewriteLocalVars(_ *QueryContext, body Body) (Body, err gen := newLocalVarGenerator("q", body) stack := newLocalDeclaredVars() body, _, err := rewriteLocalVars(gen, stack, nil, body, qc.compiler.strict) + err = append(err, stack.unused...) if len(err) != 0 { return nil, err } @@ -3984,16 +4090,21 @@ func (ci *ComprehensionIndex) String() string { return fmt.Sprintf("", NewArray(ci.Keys...)) } -func buildComprehensionIndices(dbg debug.Debug, arity func(Ref) int, candidates VarSet, rwVars map[Var]Var, node Body, result map[*Term]*ComprehensionIndex) uint64 { - var n uint64 +func buildComprehensionIndices( + dbg debug.Debug, + arity func(Ref) int, + candidates VarSet, + rwVars map[Var]Var, + node Body, + result map[*Term]*ComprehensionIndex, +) (n uint64) { cpy := candidates.Copy() vis := varVisitorPool.Get() - defer varVisitorPool.Put(vis) WalkBodies(node, func(b Body) bool { for _, expr := range b { - index := getComprehensionIndex(dbg, arity, cpy, rwVars, expr) + index := getComprehensionIndex(dbg, arity, cpy, rwVars, expr, vis) if index != nil { result[index.Term] = index n++ @@ -4001,15 +4112,24 @@ func buildComprehensionIndices(dbg debug.Debug, arity func(Ref) int, candidates // Any variables appearing in the expressions leading up to the comprehension // are fair-game to be used as index keys. vis = vis.Clear().WithParams(VarVisitorParams{SkipClosures: true, SkipRefCallHead: true}) + old := vis.vars + vis.vars = cpy vis.Walk(expr) - cpy.Update(vis.Vars()) + vis.vars = old } return false }) return n } -func getComprehensionIndex(dbg debug.Debug, arity func(Ref) int, candidates VarSet, rwVars map[Var]Var, expr *Expr) *ComprehensionIndex { +func getComprehensionIndex( + dbg debug.Debug, + arity func(Ref) int, + candidates VarSet, + rwVars map[Var]Var, + expr *Expr, + vis *VarVisitor, +) *ComprehensionIndex { // Ignore everything except = expressions. Extract // the comprehension term from the expression. if !expr.IsEquality() || expr.Negated || len(expr.With) > 0 { @@ -4062,10 +4182,14 @@ func getComprehensionIndex(dbg debug.Debug, arity func(Ref) int, candidates VarS body = x.Body } - outputs := outputVarsForBody(body, arity, ReservedVars, nil) - unsafe := body.Vars(SafetyCheckVisitorParams).Diff(outputs).Diff(ReservedVars) + vis = vis.Clear().WithParams(SafetyCheckVisitorParams) + outputs := outputVarsForBody(body, arity, ReservedVars, vis) - if len(unsafe) > 0 { + vis.Clear().WithParams(SafetyCheckVisitorParams).Walk(body) + unsafe := vis.Vars().Diff(outputs) + + if unsafe.DiffCount(ReservedVars) > 0 { + unsafe = unsafe.Diff(ReservedVars) dbg.Printf("%s: comprehension index: unsafe vars: %v", expr.Location, unsafe) return nil } @@ -4100,12 +4224,7 @@ func getComprehensionIndex(dbg debug.Debug, arity func(Ref) int, candidates VarS return nil } - result := make([]*Term, 0, len(indexVars)) - for v := range indexVars { - result = append(result, NewTerm(v)) - } - slices.SortFunc(result, TermValueCompare) - + result := util.SortedFunc(util.MapKeys(indexVars, ToTerm), TermValueCompare) debugRes := make([]*Term, len(result)) for i, r := range result { if o, ok := rwVars[r.Value.(Var)]; ok { @@ -4124,15 +4243,6 @@ type comprehensionIndexRegressionCheckVisitor struct { worse bool } -// TODO(tsandall): Improve this so that users can either supply this list explicitly -// or the information is maintained on the built-in function declaration. What we really -// need to know is whether the built-in function allows callers to push down output -// values or not. It's unlikely that anything outside of OPA does this today so this -// solution is fine for now. -var comprehensionIndexBlacklist = map[string]int{ - WalkBuiltin.Name: len(WalkBuiltin.Decl.FuncArgs().Args), -} - func newComprehensionIndexRegressionCheckVisitor(candidates VarSet) *comprehensionIndexRegressionCheckVisitor { return &comprehensionIndexRegressionCheckVisitor{ candidates: candidates, @@ -4149,7 +4259,7 @@ func (vis *comprehensionIndexRegressionCheckVisitor) visit(x any) bool { switch x := x.(type) { case *Expr: operands := x.Operands() - if pos := comprehensionIndexBlacklist[x.Operator().String()]; pos > 0 && pos < len(operands) { + if pos := comprehensionIndexDenylist[x.Operator().String()]; pos > 0 && pos < len(operands) { vis.assertEmptyIntersection(operands[pos].Vars()) } case Ref: @@ -4543,12 +4653,21 @@ func (n *TreeNode) find(ref Ref) (*TreeNode, Ref) { // DepthFirst performs a depth-first traversal of the rule tree rooted at n. If // f returns true, traversal will not continue to the children of n. +// DepthFirst calls f on n and then, in key order, on everything below it. The +// order is the map's own otherwise, and callers building an index from what they +// walk hand the order on to their results. func (n *TreeNode) DepthFirst(f func(*TreeNode) bool) { if f(n) { return } - for _, node := range n.Children { - node.DepthFirst(f) + if len(n.Children) < 2 { + for _, node := range n.Children { // no order to choose, and no slice to build + node.DepthFirst(f) + } + return + } + for _, key := range util.KeysSortedFunc(n.Children, Value.Compare) { + n.Children[key].DepthFirst(f) } } @@ -4683,8 +4802,7 @@ func (n *TreeNode) Copy() *TreeNode { } if n.Sorted != nil { - result.Sorted = make([]Value, len(n.Sorted)) - copy(result.Sorted, n.Sorted) + result.Sorted = slices.Clone(n.Sorted) } return result @@ -4791,11 +4909,9 @@ func (g *Graph) Sort() (sorted []util.T, ok bool) { temp: map[util.T]struct{}{}, } - nodesList := make([]util.T, 0, len(g.nodes)) - for node := range g.nodes { - nodesList = append(nodesList, node) - } + nodesList := util.Keys(g.nodes) sortGraphNodes(nodesList) + for _, node := range nodesList { if !sorter.Visit(node) { return nil, false @@ -4993,17 +5109,19 @@ func (vs unsafeVars) Slice() (result []unsafePair) { // If the body cannot be reordered to ensure safety, the second return value // contains a mapping of expressions to unsafe variables in those expressions. func reorderBodyForSafety(builtins map[string]*Builtin, arity func(Ref) int, globals VarSet, body Body) (Body, unsafeVars) { - vis := varVisitorPool.Get().WithParams(SafetyCheckVisitorParamsWithArity(arity)) - vis.WalkBody(body) + params := SafetyCheckVisitorParamsWithArity(arity) + vis := varVisitorPool.Get().WithParams(params) defer varVisitorPool.Put(vis) + vis.WalkBody(body) + bodyVars := vis.Vars().Copy() safe := bodyVars.Intersect(globals) unsafe := make(unsafeVars, len(bodyVars)-len(safe)) for _, e := range body { - vis = vis.Clear().WithParams(SafetyCheckVisitorParamsWithArity(arity)) + vis = vis.Clear().WithParams(params) vis.Walk(e) for v := range vis.Vars() { if _, ok := safe[v]; !ok { @@ -5032,15 +5150,19 @@ func reorderBodyForSafety(builtins map[string]*Builtin, arity func(Ref) int, glo cv := unsVis.Vars().Intersect(bodyVars).Diff(globals) unsVis.Clear() - ob := outputVarsForBody(reordered, arity, safe, vis) + // ob is the expensive part of this loop, and an empty cv makes the + // comparisons below hold whatever it is. + if len(cv) > 0 { + ob := outputVarsForBody(reordered, arity, safe, vis) - if cv.DiffCount(ob) > 0 { - uv := cv.Diff(ob) - if uv.Equal(ovs) { // special case "closure-self" - continue + if cv.DiffCount(ob) > 0 { + uv := cv.Diff(ob) + if uv.Equal(ovs) { // special case "closure-self" + continue + } + // The expression is closing over variables not yet present in reordered body + unsafe.Set(e, uv) } - // The expression is closing over variables not yet present in reordered body - unsafe.Set(e, uv) } for v := range unsafe[e] { @@ -5066,19 +5188,20 @@ func reorderBodyForSafety(builtins map[string]*Builtin, arity func(Ref) int, glo // Recursively visit closures and perform the safety checks on them. // Update the globals at each expression to include the variables that could // be closed over. - g := globals.Copy() xform := newBodySafetyTransformer(builtins, arity) xform.gv = NewGenericVisitor(xform.Visit) + xform.unsafe = unsafe + xform.globals = globals.Copy() + + vis = vis.WithParams(params) + vis.vars = xform.globals + for i, e := range reordered { if i > 0 { - vis = vis.Clear().WithParams(SafetyCheckVisitorParamsWithArity(arity)) vis.Walk(reordered[i-1]) - g.Update(vis.Vars()) } xform.current = e - xform.globals = g - xform.unsafe = unsafe xform.gv.Walk(e) } @@ -5173,7 +5296,7 @@ func unsafeImplicitBodyVars(body Body, arity func(Ref) int) VarSet { } if e.IsEquality() { - for v := range outputVarsForExprEq(e, VarSet{}, VarSet{}) { + for v := range outputVarsForExprEq(e, VarSet{}, nil) { bindings[v] = struct{}{} } continue @@ -5241,22 +5364,24 @@ func (xform *bodySafetyTransformer) Visit(x any) bool { case *Term: switch x := term.Value.(type) { case *object: - cpy, _ := x.Map(func(k, v *Term) (*Term, *Term, error) { - kcpy := k.Copy() - xform.gv.Walk(kcpy) - vcpy := v.Copy() - xform.gv.Walk(vcpy) - return kcpy, vcpy, nil - }) - term.Value = cpy + if !x.IsGround() { + term.Value, _ = x.Map(func(k, v *Term) (*Term, *Term, error) { + kcpy := k.Copy() + xform.gv.Walk(kcpy) + vcpy := v.Copy() + xform.gv.Walk(vcpy) + return kcpy, vcpy, nil + }) + } return true case *set: - cpy, _ := x.Map(func(v *Term) (*Term, error) { - vcpy := v.Copy() - xform.gv.Walk(vcpy) - return vcpy, nil - }) - term.Value = cpy + if !x.IsGround() { + term.Value, _ = x.Map(func(v *Term) (*Term, error) { + vcpy := v.Copy() + xform.gv.Walk(vcpy) + return vcpy, nil + }) + } return true case *ArrayComprehension: xform.reorderArrayComprehensionSafety(x) @@ -5428,6 +5553,13 @@ func outputVarsForExprEq(expr *Expr, safe VarSet, output VarSet) VarSet { return safe } + // Clear the shared buffer before use. Callers of outputVarsForExpr reuse the + // same VarSet across candidate expressions in a single reorderBodyForSafety + // pass. Without this, leftover bindings from an earlier (not-yet-schedulable) + // call expression can leak into Unify() via the safe basis and incorrectly + // mark an equality as grounded. See issue #8302. + clear(output) + output = outputVarsForTerms(expr, safe, output) output.Update(safe) if expr.fromAssignment { @@ -5468,8 +5600,15 @@ func outputVarsForExprCall(expr *Expr, arity int, safe VarSet, terms []*Term, vi vis = ClearOrNewVarVisitor(vis).WithParams(params) vis.WalkArgs(Args(terms[:numInputTerms])) - unsafe := vis.Vars().Diff(output).DiffCount(safe) - if unsafe > 0 { + unsafe := vis.Vars() + for i := range output { + delete(unsafe, i) + } + for i := range safe { + delete(unsafe, i) + } + + if len(unsafe) > 0 { return VarSet{} } @@ -5520,9 +5659,6 @@ func (f *equalityFactory) Generate(other *Term) *Expr { return expr } -// TODO: Move to internal package? -const LocalVarPrefix = "__local" - type localVarGenerator struct { exclude VarSet suffix string @@ -5650,8 +5786,6 @@ type usedRef struct { } func resolveRefsInRule(globals map[Var]*usedRef, rule *Rule) error { - ignore := &declaredVarStack{} - vars := NewVarSet() var vis *GenericVisitor var err error @@ -5677,30 +5811,27 @@ func resolveRefsInRule(globals map[Var]*usedRef, rule *Rule) error { return true case *Term: - if _, ok := x.Value.(Ref); ok { - if RootDocumentRefs.Contains(x) { - // We could support args named input, data, etc. however - // this would require rewriting terms in the head and body. - // Preventing root document shadowing is simpler, and - // arguably, will prevent confusing names from being used. - // NOTE: this check is also performed as part of strict-mode in - // checkRootDocumentOverrides. - err = fmt.Errorf("args must not shadow %v (use a different variable name)", x) - return true - } + if TermValueIs[Ref](x) && RootDocumentRefs.Contains(x) { + // We could support args named input, data, etc. however + // this would require rewriting terms in the head and body. + // Preventing root document shadowing is simpler, and + // arguably, will prevent confusing names from being used. + // NOTE: this check is also performed as part of strict-mode in + // checkRootDocumentOverrides. + err = fmt.Errorf("args must not shadow %v (use a different variable name)", x) + return true } } return false }) - vis.Walk(rule.Head.Args) if err != nil { return err } - ignore.Push(vars) - ignore.Push(declaredVars(rule.Body)) + ignore := &declaredVarStack{} + ignore.Push(vars, declaredBodyVars(rule.Body)) ref := rule.Head.Ref() for i := 1; i < len(ref); i++ { @@ -5745,12 +5876,12 @@ func resolveRefsInExpr(globals map[Var]*usedRef, ignore *declaredVarStack, expr } } case *Every: - locals := NewVarSet() + vis := varVisitorPool.Get() if ts.Key != nil { - locals.Update(ts.Key.Vars()) + vis.Walk(ts.Key) } - locals.Update(ts.Value.Vars()) - ignore.Push(locals) + vis.Walk(ts.Value) + ignore.Push(vis.Vars()) cpy.Terms = &Every{ Key: ts.Key.Copy(), // TODO(sr): do more? Value: ts.Value.Copy(), // TODO(sr): do more? @@ -5758,6 +5889,7 @@ func resolveRefsInExpr(globals map[Var]*usedRef, ignore *declaredVarStack, expr Body: resolveRefsInBody(globals, ignore, ts.Body), } ignore.Pop() + varVisitorPool.Put(vis) case *Not: cpy.Terms = &Not{ Body: resolveRefsInBody(globals, ignore, ts.Body), @@ -5801,9 +5933,8 @@ func resolveRefsInTerm(globals map[Var]*usedRef, ignore *declaredVarStack, term } return term case Ref: - fqn := resolveRef(globals, ignore, v) cpy := *term - cpy.Value = fqn + cpy.Value = resolveRef(globals, ignore, v) return &cpy case *object: cpy := *term @@ -5822,38 +5953,37 @@ func resolveRefsInTerm(globals map[Var]*usedRef, ignore *declaredVarStack, term cpy.Value = Call(resolveRefsInTermSlice(globals, ignore, v)) return &cpy case Set: - s, _ := v.Map(func(e *Term) (*Term, error) { + cpy := *term + cpy.Value, _ = v.Map(func(e *Term) (*Term, error) { return resolveRefsInTerm(globals, ignore, e), nil }) - cpy := *term - cpy.Value = s return &cpy case *ArrayComprehension: - ac := &ArrayComprehension{} - ignore.Push(declaredVars(v.Body)) - ac.Term = resolveRefsInTerm(globals, ignore, v.Term) - ac.Body = resolveRefsInBody(globals, ignore, v.Body) + ignore.Push(declaredBodyVars(v.Body)) cpy := *term - cpy.Value = ac + cpy.Value = &ArrayComprehension{ + Term: resolveRefsInTerm(globals, ignore, v.Term), + Body: resolveRefsInBody(globals, ignore, v.Body), + } ignore.Pop() return &cpy case *ObjectComprehension: - oc := &ObjectComprehension{} - ignore.Push(declaredVars(v.Body)) - oc.Key = resolveRefsInTerm(globals, ignore, v.Key) - oc.Value = resolveRefsInTerm(globals, ignore, v.Value) - oc.Body = resolveRefsInBody(globals, ignore, v.Body) + ignore.Push(declaredBodyVars(v.Body)) cpy := *term - cpy.Value = oc + cpy.Value = &ObjectComprehension{ + Key: resolveRefsInTerm(globals, ignore, v.Key), + Value: resolveRefsInTerm(globals, ignore, v.Value), + Body: resolveRefsInBody(globals, ignore, v.Body), + } ignore.Pop() return &cpy case *SetComprehension: - sc := &SetComprehension{} - ignore.Push(declaredVars(v.Body)) - sc.Term = resolveRefsInTerm(globals, ignore, v.Term) - sc.Body = resolveRefsInBody(globals, ignore, v.Body) + ignore.Push(declaredBodyVars(v.Body)) cpy := *term - cpy.Value = sc + cpy.Value = &SetComprehension{ + Term: resolveRefsInTerm(globals, ignore, v.Term), + Body: resolveRefsInBody(globals, ignore, v.Body), + } ignore.Pop() return &cpy case *TemplateString: @@ -5903,12 +6033,8 @@ func (s declaredVarStack) Contains(v Var) bool { return false } -func (s declaredVarStack) Add(v Var) { - s[len(s)-1].Add(v) -} - -func (s *declaredVarStack) Push(vs VarSet) { - *s = append(*s, vs) +func (s *declaredVarStack) Push(vs ...VarSet) { + *s = append(*s, vs...) } func (s *declaredVarStack) Pop() { @@ -5916,16 +6042,31 @@ func (s *declaredVarStack) Pop() { *s = curr[:len(curr)-1] } -func declaredVars(x any) VarSet { +func declaredBodyVars(body Body) VarSet { vars := NewVarSet() + for _, e := range body { + vars = declaredVars(e, vars) + } + return vars +} + +func declaredVars(x any, vars VarSet) VarSet { + if vars == nil { + vars = NewVarSet() + } vis := NewGenericVisitor(func(x any) bool { switch x := x.(type) { case *Expr: if x.IsAssignment() && validEqAssignArgCount(x) { - WalkVars(x.Operand(0), func(v Var) bool { + lhs := x.Operand(0) + if v, ok := lhs.Value.(Var); ok { vars.Add(v) - return false - }) + } else { + WalkVars(lhs, func(v Var) bool { + vars.Add(v) + return false + }) + } } else if decl, ok := x.Terms.(*SomeDecl); ok { for i := range decl.Symbols { switch val := decl.Symbols[i].Value.(type) { @@ -6017,16 +6158,15 @@ func rewriteComprehensionTerms(f *equalityFactory, node any) (any, error) { func rewriteEquals(x any) (modified bool) { // Note: can't use Interned.Refs.Equality here as this may be mutated unifyOp := Equality.Ref() - t := NewGenericTransformer(func(x any) (any, error) { + t := GenericTransformer{f: func(x any) (any, error) { if x, ok := x.(*Expr); ok && x.IsCall() { - operator := x.Operator() - if operator.Equal(Interned.Refs.Equal) && len(x.Operands()) == 2 { + if x.Operator().Equal(Interned.Refs.Equal) && len(x.Operands()) == 2 { modified = true x.SetOperator(NewTerm(unifyOp)) } } return x, nil - }) + }} _, _ = Transform(t, x) // ignore error return modified } @@ -6044,7 +6184,8 @@ func rewriteTestEqualities(f *equalityFactory, body Body) Body { result, terms[2] = rewriteDynamicsShallow(expr, f, terms[2], result) case expr.IsEvery(): // We rewrite equalities inside of every-bodies as a fail here will be the cause of the test-rule fail. - // Failures inside other expressions with closures, such as comprehensions, won't cause the test-rule to fail, so we skip those. + // Failures inside other expressions with closures, such as comprehensions, won't cause the test-rule to + // fail, so we skip those. every := expr.Terms.(*Every) every.Body = rewriteTestEqualities(f, every.Body) } @@ -6201,13 +6342,13 @@ func rewriteDynamicsOne(original *Expr, f *equalityFactory, term *Term, result B }) return result, NewTerm(cpy).SetLocation(term.Location) case Set: - cpy := NewSet() + terms := make([]*Term, 0, v.Len()) for _, term := range v.Slice() { var rw *Term result, rw = rewriteDynamicsOne(original, f, term, result) - cpy.Add(rw) + terms = append(terms, rw) } - return result, NewTerm(cpy).SetLocation(term.Location) + return result, SetTerm(terms...).SetLocation(term.Location) case *ArrayComprehension: var extra *Expr v.Body, extra = rewriteDynamicsComprehensionBody(original, f, v.Body, term) @@ -6502,6 +6643,10 @@ type localDeclaredVars struct { // indicates if an assignment (:= operator) has been seen *ever* assignment bool + + // strict-mode diagnostics for assigned and declared vars that are never used, + // kept apart from the rewrite errors because they're recoverable + unused Errors } type varOccurrence uint8 @@ -6553,6 +6698,7 @@ func (s *localDeclaredVars) Clear() { clear(s.rewritten) s.vars = s.vars[:0] + s.unused = nil if vs != nil { s.vars = append(s.vars, vs.clear()) @@ -6706,13 +6852,15 @@ func rewriteDeclaredVarsInBody(g *localVarGenerator, stack *localDeclaredVars, u cpy.Append(NewExpr(BooleanTerm(true))) } - errs = checkUnusedAssignedVars(body, stack, used, errs, strict) - return cpy, checkUnusedDeclaredVars(body, stack, used, cpy, errs) + checkUnusedAssignedVars(body, stack, used, errs, strict) + checkUnusedDeclaredVars(body, stack, used, cpy, errs) + + return cpy, errs } -func checkUnusedAssignedVars(body Body, stack *localDeclaredVars, used VarSet, errs Errors, strict bool) Errors { - if !strict || len(errs) > 0 { - return errs +func checkUnusedAssignedVars(body Body, stack *localDeclaredVars, used VarSet, errs Errors, strict bool) { + if !strict || len(errs) > 0 || len(stack.unused) > 0 { + return } dvs := stack.Peek() @@ -6724,31 +6872,31 @@ func checkUnusedAssignedVars(body Body, stack *localDeclaredVars, used VarSet, e } } if !hasAssignedVars { - return errs + return } - unused := NewVarSet() + var unused VarSet for v, occ := range dvs.occurrence { // A var that was assigned in this scope must have been seen (used) more than once (the time of assignment) in // the same, or nested, scope to be counted as used. - if !v.IsWildcard() && stack.Count(v) <= 1 && occ == assignedVar { + if !v.IsWildcard() && stack.Count(v) <= 1 && occ == assignedVar && !used.Contains(dvs.vs[v]) { + if unused == nil { + unused = NewVarSet() + } unused.Add(dvs.vs[v]) } } - rewrittenUsed := NewVarSet() + // rewritten unused for v := range used { if gv, ok := stack.Declared(v); ok { - rewrittenUsed.Add(gv) - } else { - rewrittenUsed.Add(v) + delete(unused, gv) } } - unused = unused.Diff(rewrittenUsed) if len(unused) == 0 { - return errs + return } reversed := make(map[Var]Var, len(dvs.vs)) @@ -6760,25 +6908,22 @@ func checkUnusedAssignedVars(body Body, stack *localDeclaredVars, used VarSet, e found := false for i := range body { if body[i].Vars(VarVisitorParams{}).Contains(gv) { - errs = append(errs, NewError(CompileErr, body[i].Loc(), "assigned var %v unused", reversed[gv])) + stack.unused = append(stack.unused, NewError(CompileErr, body[i].Loc(), "assigned var %v unused", reversed[gv])) found = true break } } if !found { - errs = append(errs, NewError(CompileErr, body[0].Loc(), "assigned var %v unused", reversed[gv])) + stack.unused = append(stack.unused, NewError(CompileErr, body[0].Loc(), "assigned var %v unused", reversed[gv])) } } - - return errs } -func checkUnusedDeclaredVars(body Body, stack *localDeclaredVars, used VarSet, cpy Body, errs Errors) Errors { - +func checkUnusedDeclaredVars(body Body, stack *localDeclaredVars, used VarSet, cpy Body, errs Errors) { // NOTE(tsandall): Do not generate more errors if there are existing // declaration errors. - if len(errs) > 0 { - return errs + if len(errs) > 0 || len(stack.unused) > 0 { + return } dvs := stack.Peek() @@ -6790,7 +6935,7 @@ func checkUnusedDeclaredVars(body Body, stack *localDeclaredVars, used VarSet, c } } if !hasDeclaredVars { - return errs + return } declared := NewVarSet() @@ -6813,7 +6958,7 @@ func checkUnusedDeclaredVars(body Body, stack *localDeclaredVars, used VarSet, c dbv := declared.Diff(bodyvars) if dbv.DiffCount(used) == 0 { - return errs + return } reversed := make(map[Var]Var, len(dvs.vs)) @@ -6821,30 +6966,35 @@ func checkUnusedDeclaredVars(body Body, stack *localDeclaredVars, used VarSet, c reversed[v] = k } + var varsDeclaredInExpr VarSet + for _, gv := range dbv.Diff(used).Sorted() { rv := reversed[gv] if !rv.IsGenerated() { // Scan through body exprs, looking for a match between the // bad var's original name, and each expr's declared vars. foundUnusedVarByName := false + if varsDeclaredInExpr == nil { + varsDeclaredInExpr = NewVarSet() + } + for i := range body { - varsDeclaredInExpr := declaredVars(body[i]) + clear(varsDeclaredInExpr) + varsDeclaredInExpr = declaredVars(body[i], varsDeclaredInExpr) if varsDeclaredInExpr.Contains(rv) { // TODO(philipc): Clean up the offset logic here when the parser // reports more accurate locations. - errs = append(errs, NewError(CompileErr, body[i].Loc(), "declared var %v unused", rv)) + stack.unused = append(stack.unused, NewError(CompileErr, body[i].Loc(), "declared var %v unused", rv)) foundUnusedVarByName = true break } } // Default error location returned. if !foundUnusedVarByName { - errs = append(errs, NewError(CompileErr, body[0].Loc(), "declared var %v unused", rv)) + stack.unused = append(stack.unused, NewError(CompileErr, body[0].Loc(), "declared var %v unused", rv)) } } } - - return errs } func rewriteEveryStatement(g *localVarGenerator, stack *localDeclaredVars, expr *Expr, errs Errors, strict bool) (*Expr, Errors) { @@ -6930,12 +7080,6 @@ func rewriteSomeDeclStatement(g *localVarGenerator, stack *localDeclaredVars, ex return nil, errs } -const ( - errAssignInNegated = "cannot assign vars inside negated expression" - errAssignInAndOperand = "cannot assign vars inside implicit and operand" - errAssignInOrOperand = "cannot assign vars inside implicit or operand" -) - func rewriteNotStatement(g *localVarGenerator, stack *localDeclaredVars, expr *Expr, errs Errors, strict bool) (*Expr, Errors) { if not := expr.Terms.(*Not); !not.ExplicitBody { // Only explicit not bodies are allowed to declare vars. @@ -7016,9 +7160,9 @@ func rewriteDeclaredVarsInImplicitBody(g *localVarGenerator, stack *localDeclare } func rewriteDeclaredVarsInExpr(g *localVarGenerator, stack *localDeclaredVars, expr *Expr, errs Errors, strict bool) (*Expr, Errors) { - vis := NewGenericVisitor(func(x any) bool { - var stop bool - // Note: we don't include *Not nodes here, as such bodies are allowed to contain assignments; e.g. 'not {x := input.x; f(x)}' + vis := NewGenericVisitor(func(x any) (stop bool) { + // Note: we don't include *Not nodes here, as such bodies are + // allowed to contain assignments; e.g. 'not {x := input.x; f(x)}' switch x := x.(type) { case *Term: stop, errs = rewriteDeclaredVarsInTerm(g, stack, x, errs, strict) @@ -7032,18 +7176,16 @@ func rewriteDeclaredVarsInExpr(g *localVarGenerator, stack *localDeclaredVars, e } func rewriteDeclaredAssignment(g *localVarGenerator, stack *localDeclaredVars, expr *Expr, errs Errors, strict bool) (*Expr, Errors) { - if expr.Negated { - errs = append(errs, newErrorString(CompileErr, expr.Location, errAssignInNegated)) - return expr, errs + return expr, append(errs, newErrorString(CompileErr, expr.Location, errAssignInNegated)) } - numErrsBefore := len(errs) - if !validEqAssignArgCount(expr) { return expr, errs } + numErrsBefore := len(errs) + // Rewrite terms on right hand side capture seen vars and recursively // process comprehensions before left hand side is processed. Also // rewrite with modifier. @@ -7102,7 +7244,10 @@ func rewriteDeclaredVarsInTerm(g *localVarGenerator, stack *localDeclaredVars, t switch v := term.Value.(type) { case Var: if gv, ok := stack.Declared(v); ok { - term.Value = gv + // don't allocate for boxing Var -> Value unless it changed + if gv != v { + term.Value = gv + } stack.Seen(v) } else if stack.Occurrence(v) == newVar { stack.Insert(v, v, seenVar) @@ -7111,8 +7256,10 @@ func rewriteDeclaredVarsInTerm(g *localVarGenerator, stack *localDeclaredVars, t if RootDocumentRefs.Contains(term) { x := v[0].Value.(Var) if occ, ok := stack.GlobalOccurrence(x); ok && occ != seenVar { - gv, _ := stack.Declared(x) - term.Value = gv + // don't allocate for boxing Var -> Value unless it changed + if gv, _ := stack.Declared(x); gv != x { + term.Value = gv + } } return true, errs @@ -7130,20 +7277,26 @@ func rewriteDeclaredVarsInTerm(g *localVarGenerator, stack *localDeclaredVars, t }) return false, errs case *object: - cpy, _ := v.Map(func(k, v *Term) (*Term, *Term, error) { - kcpy := k.Copy() - errs = rewriteDeclaredVarsInTermRecursive(g, stack, kcpy, errs, strict) - errs = rewriteDeclaredVarsInTermRecursive(g, stack, v, errs, strict) + term.Value, _ = v.Map(func(k, v *Term) (*Term, *Term, error) { + kcpy := k + if !IsScalar(k.Value) { + kcpy = k.Copy() + errs = rewriteDeclaredVarsInTermRecursive(g, stack, kcpy, errs, strict) + } + if !IsScalar(v.Value) { + errs = rewriteDeclaredVarsInTermRecursive(g, stack, v, errs, strict) + } return kcpy, v, nil }) - term.Value = cpy - case Set: - cpy, _ := v.Map(func(elem *Term) (*Term, error) { + case *set: + term.Value, _ = v.Map(func(elem *Term) (*Term, error) { + if IsScalar(elem.Value) { + return elem, nil + } elemcpy := elem.Copy() errs = rewriteDeclaredVarsInTermRecursive(g, stack, elemcpy, errs, strict) return elemcpy, nil }) - term.Value = cpy case *ArrayComprehension: errs = rewriteDeclaredVarsInArrayComprehension(g, stack, v, errs, strict) case *SetComprehension: @@ -7157,8 +7310,7 @@ func rewriteDeclaredVarsInTerm(g *localVarGenerator, stack *localDeclaredVars, t } func rewriteDeclaredVarsInTermRecursive(g *localVarGenerator, stack *localDeclaredVars, term *Term, errs Errors, strict bool) Errors { - WalkTerms(term, func(t *Term) bool { - var stop bool + WalkTerms(term, func(t *Term) (stop bool) { stop, errs = rewriteDeclaredVarsInTerm(g, stack, t, errs, strict) return stop }) @@ -7388,7 +7540,7 @@ func validateWithBuiltinTarget(bi *Builtin, target Ref, loc *location.Location) } switch { - case target.HasPrefix(Ref([]*Term{VarTerm("internal")})): + case len(target) > 0 && Var("internal").Equal(target[0].Value): return NewError(CompileErr, loc, "with keyword replacing built-in function: replacement of internal function %q invalid", target) case bi.Relation: @@ -7437,12 +7589,30 @@ func safetyErrorSlice(unsafe unsafeVars, rewritten map[Var]Var, scope string) (r return } - for _, pair := range unsafe.Vars() { + assignmentLHS := assignmentLHSVars(unsafe, rewritten) + + pairs := unsafe.Vars() + hasNonAssignmentLHS := false + for _, pair := range pairs { + v := pair.Var + if w, ok := rewritten[v]; ok { + v = w + } + if !v.IsGenerated() && !assignmentLHS.Contains(v) && !assignmentLHS.Contains(pair.Var) { + hasNonAssignmentLHS = true + break + } + } + + for _, pair := range pairs { v := pair.Var if w, ok := rewritten[v]; ok { v = w } if !v.IsGenerated() { + if hasNonAssignmentLHS && (assignmentLHS.Contains(v) || assignmentLHS.Contains(pair.Var)) { + continue + } if _, ok := allFutureKeywords[string(v)]; ok { result = append(result, NewError(UnsafeVarErr, pair.Loc, "var %[1]v is unsafe%[2]v (hint: `import future.keywords.%[1]v` to import a future keyword)", v, scope)) @@ -7459,14 +7629,14 @@ func safetyErrorSlice(unsafe unsafeVars, rewritten map[Var]Var, scope string) (r // If the expression contains unsafe generated variables, report which // expressions are unsafe instead of the variables that are unsafe (since // the latter are not meaningful to the user.) - pairs := util.SortedFunc(unsafe.Slice(), func(a, b unsafePair) int { + exprPairs := util.SortedFunc(unsafe.Slice(), func(a, b unsafePair) int { return a.Expr.Location.Compare(b.Expr.Location) }) // Report at most one error per generated variable. seen := NewVarSet() - for _, expr := range pairs { + for _, expr := range exprPairs { before := len(seen) for v := range expr.Vars { if v.IsGenerated() { @@ -7481,6 +7651,23 @@ func safetyErrorSlice(unsafe unsafeVars, rewritten map[Var]Var, scope string) (r return } +func assignmentLHSVars(unsafe unsafeVars, rewritten map[Var]Var) VarSet { + lhs := NewVarSet() + for expr := range unsafe { + if !expr.fromAssignment || !validEqAssignArgCount(expr) { + continue + } + WalkVars(expr.Operand(0), func(v Var) bool { + lhs.Add(v) + if w, ok := rewritten[v]; ok { + lhs.Add(w) + } + return false + }) + } + return lhs +} + // ruleScopes resolves the "in rule ..." label appended to safety errors for the // rules of one module, which is only added where a line holds rules of more than // one name and the location alone is ambiguous. Its index of those lines is built diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/env.go b/vendor/github.com/open-policy-agent/opa/v1/ast/env.go index ff28b58f4d..98c8f4b20f 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/env.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/env.go @@ -69,12 +69,7 @@ func (env *TypeEnv) GetByValue(v Value) types.Type { static[i] = env.GetByValue(x.Elem(i).Value) } - var dynamic types.Type - if len(static) == 0 { - dynamic = types.A - } - - return types.NewArray(static, dynamic) + return types.NewArray(static, nil) case *lazyObj: return env.GetByValue(x.force()) @@ -93,10 +88,6 @@ func (env *TypeEnv) GetByValue(v Value) types.Type { dynamic = types.NewDynamicProperty(env.GetByValue(k.Value), env.GetByValue(v.Value)) }) - if len(static) == 0 && dynamic == nil { - dynamic = types.NewDynamicProperty(types.A, types.A) - } - return types.NewObject(static, dynamic) case *set: @@ -104,9 +95,6 @@ func (env *TypeEnv) GetByValue(v Value) types.Type { x.Foreach(func(elem *Term) { tpe = types.Or(tpe, env.GetByValue(elem.Value)) }) - if tpe == nil { - tpe = types.A - } return types.NewSet(tpe) // Comprehensions. @@ -383,6 +371,8 @@ func (n *typeTreeNode) Insert(path Ref, tpe types.Type, env *TypeEnv) { // with an types.Or, instead of being merged. // If 'a' is an Any containing an Object, and 'b' is an Object (or vice versa); AND both objects have no // static properties, they are merged. +// If either object has neither static nor dynamic properties, it is the empty object type, and the other +// type is returned unchanged. // If 'a' and 'b' are different types, they are joined with an types.Or. func mergeTypes(a, b types.Type) types.Type { if a == nil { @@ -403,25 +393,33 @@ func mergeTypes(a, b types.Type) types.Type { switch a := a.(type) { case *types.Object: + aDynProps := a.DynamicProperties() if bObj, ok := b.(*types.Object); ok && len(a.StaticProperties()) == 0 && len(bObj.StaticProperties()) == 0 { - if len(a.StaticProperties()) > 0 || len(bObj.StaticProperties()) > 0 { - return types.Or(a, bObj) + bDynProps := bObj.DynamicProperties() + + // An object type with neither static nor dynamic properties is the + // empty object, which the other object type already covers. + if aDynProps == nil { + return bObj + } + if bDynProps == nil { + return a } - aDynProps := a.DynamicProperties() - bDynProps := bObj.DynamicProperties() dynProps := types.NewDynamicProperty( types.Or(aDynProps.Key, bDynProps.Key), mergeTypes(aDynProps.Value, bDynProps.Value), ) return types.NewObject(nil, dynProps) - } else if bAny, ok := b.(types.Any); ok && len(a.StaticProperties()) == 0 { + } else if bAny, ok := b.(types.Any); ok && len(a.StaticProperties()) == 0 && aDynProps != nil { // If a is an object type with no static components ... for _, t := range bAny { if tObj, ok := t.(*types.Object); ok && len(tObj.StaticProperties()) == 0 { // ... and b is a types.Any containing an object with no static components, we merge them. - aDynProps := a.DynamicProperties() tDynProps := tObj.DynamicProperties() + if tDynProps == nil { + continue + } tDynProps.Key = types.Or(tDynProps.Key, aDynProps.Key) tDynProps.Value = types.Or(tDynProps.Value, aDynProps.Value) return bAny diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/errors.go b/vendor/github.com/open-policy-agent/opa/v1/ast/errors.go index 1188cd6e08..a1ea433683 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/errors.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/errors.go @@ -9,6 +9,8 @@ import ( "slices" "strconv" "strings" + + "github.com/open-policy-agent/opa/v1/util" ) // Errors represents a series of errors encountered during parsing, compiling, @@ -16,21 +18,14 @@ import ( type Errors []*Error func (e Errors) Error() string { - if len(e) == 0 { return "no error(s)" } - if len(e) == 1 { - return fmt.Sprintf("1 error occurred: %v", e[0].Error()) + return "1 error occurred: " + e[0].Error() } - s := make([]string, len(e)) - for i, err := range e { - s[i] = err.Error() - } - - return fmt.Sprintf("%d errors occurred:\n%s", len(e), strings.Join(s, "\n")) + return fmt.Sprintf("%d errors occurred:\n%s", len(e), strings.Join(util.Map(e, (*Error).Error), "\n")) } // Sort sorts the error slice by location. If the locations are equal then the @@ -67,10 +62,8 @@ const ( // IsError returns true if err is an AST error with code. func IsError(code string, err error) bool { - if err, ok := err.(*Error); ok { - return err.Code == code - } - return false + e, ok := err.(*Error) + return ok && e.Code == code } // ErrorDetails defines the interface for detailed error messages. @@ -90,7 +83,6 @@ func (e *Error) Error() string { var prefix string if e.Location != nil { - if len(e.Location.File) > 0 { prefix += e.Location.File + ":" + strconv.Itoa(e.Location.Row) } else { diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/index.go b/vendor/github.com/open-policy-agent/opa/v1/ast/index.go index 3c0c89e723..ea96d6f00b 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/index.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/index.go @@ -5,7 +5,9 @@ package ast import ( + "cmp" "maps" + "math/bits" "slices" "strings" "sync" @@ -54,6 +56,25 @@ type ( defaultRule *Rule kind RuleKind onlyGroundRefs bool + // rules holds one entry per rule and else branch the trie carries, groups + // the position of its ruleset among the rules Build was given. Reading the + // ids a lookup reached in increasing order groups them and orders each + // group by priority; see trieTraversalResult and gather. + rules []*Rule + groups []int32 + // required holds, per rule id, the refs it needs defined that are not + // trie levels, as positions in requiredRefs. See refindices.partition. + required map[int32][]int32 + // requiredRefs names those refs, indexed by the positions required holds. + // Numbering them keeps a lookup's memo a lookup by position rather than a + // search: a rule reading a ref of its own is the shape that made the memo + // a linear scan over one entry per rule. + requiredRefs []Ref + // memberships holds, per rule id, the collections gather consults; recorded + // by refindices.recordMembership. + memberships map[int32][]membership + // mayEarlyExit decides whether consulting a collection pays; see gather. + mayEarlyExit bool } ) @@ -81,7 +102,7 @@ func (i *baseDocEqIndex) Build(rules []*Rule) bool { } i.kind = rules[0].Head.RuleKind() - indices := newrefindices(i.isVirtual) + indices := newrefindices(i.isVirtual, newRefTable()) values := make(map[Var]Value) // build indices for each rule. @@ -105,159 +126,427 @@ func (i *baseDocEqIndex) Build(rules []*Rule) bool { } // build trie out of indices. - sorted := indices.Sorted() + levels, unvalued := indices.partition(indices.Sorted()) for idx := range rules { - var prio int WalkRules(rules[idx], func(rule *Rule) bool { if rule.Default { return false } + + // Ids are minted in WalkRules' order, so they ascend with priority + // within a ruleset -- the (insertion, priority) pair a node used to + // carry, in one integer: + // + // f(x) := 1 if x == "a" # group 0, id 0 + // else := 2 if x == "b" # id 1 + // f(x) := 3 if x == "c" # group 1, id 2 + id := int32(len(i.rules)) + i.rules = append(i.rules, rule) + i.groups = append(i.groups, int32(idx)) + + if ms := indices.memberships[rule]; len(ms) > 0 { + if i.memberships == nil { + i.memberships = make(map[int32][]membership, len(rules)) + } + i.memberships[id] = ms + } + // Each set of indices the rule can be reached through gets its own - // path. They share a priority, so a lookup arriving at the rule down + // path. They share an id, so a lookup arriving at the rule down // several of them still reports it once (see trieTraversalResult.Add). - if len(indices.disjunctions[rule]) == 0 { - i.insertPath(sorted, indices.rules[rule], [...]int{idx, prio}, rule) + paths := indices.disjunctions[rule] + if len(paths) == 0 { + i.insertPath(indices.table, levels, indices.rules[rule], id, rule) + i.require(indices.table, id, unvalued, alternatives{indices.rules[rule]}) } else { - for _, path := range indices.paths(rule) { - i.insertPath(sorted, path, [...]int{idx, prio}, rule) + alts := indices.paths(rule) + for _, path := range alts { + i.insertPath(indices.table, levels, path, id, rule) } + i.require(indices.table, id, unvalued, alts) } - prio++ return false }) } + + i.root.compact() + i.mayEarlyExit = mayEarlyExit(i.rules) + return true } -func (i *baseDocEqIndex) insertPath(sorted []Ref, path []*refindex, prio [2]int, rule *Rule) { +// mayEarlyExit reports whether a caller could stop at the first of these rules +// that holds. Build asks it of every rule, which is what gather can know before it +// has candidates; Lookup asks resultMayEarlyExit of the candidates, which is finer. +func mayEarlyExit(rules []*Rule) bool { + var value Value + return agreeOnValue(rules, &value) +} + +// require records the refs rule needs defined, of those partition kept out of the +// trie. Only a ref every path to the rule reads is recorded: one an `or` reads on +// a single alternative does not have to hold for the rule to match, and a lookup +// that dropped the rule over it would lose an answer rather than a shortcut. +func (i *baseDocEqIndex) require(table *refTable, id int32, unvalued []refID, paths alternatives) { + if len(unvalued) == 0 || len(paths) == 0 { + return + } + + var required []int32 + for pos, ref := range unvalued { + reads := func(path []*refindex) bool { + return slices.ContainsFunc(path, func(ri *refindex) bool { return ri.ref == ref }) + } + if !slices.ContainsFunc(paths, func(path []*refindex) bool { return !reads(path) }) { + required = append(required, int32(pos)) + } + } + + if len(required) > 0 { + if i.required == nil { + i.required = make(map[int32][]int32, len(unvalued)) + i.requiredRefs = make([]Ref, len(unvalued)) + for pos, ref := range unvalued { + i.requiredRefs[pos] = table.ref(ref) + } + } + i.required[id] = required + } +} + +func (i *baseDocEqIndex) insertPath(table *refTable, levels []refID, path []*refindex, id int32, rule *Rule) { node := i.root - if len(path) > 0 { - for _, ref := range sorted { - var values []*refindex - for _, ri := range path { - if ri.Ref.Equal(ref) { - values = append(values, ri) - } + // The path stops at the last level it constrains. A rule that constrains + // nothing below has nothing to test there, so walking on would only pad the + // path with an "absent" node per remaining level -- a copy of the whole tail + // that no other rule shares, which is what made a trie of n levels cost n^2 + // nodes to build and to walk. The multiple-scalar case below has always + // attached mid-trie for the same reason. + remaining := len(path) + + // One scratch slice for every level, not one per level: a rule's path crosses + // every level above the last it constrains, most of them constraining nothing. + var values []*refindex + + for _, level := range levels { + if remaining == 0 { + break + } + + values = values[:0] + for _, ri := range path { + if ri.ref == level { + values = append(values, ri) } - if len(values) == 0 { - node = node.Insert(ref, nil, nil) - } else if len(values) == 1 { - node = node.Insert(ref, values[0].Value, values[0].Mapper) - } else { - if slices.ContainsFunc(values, (*refindex).isVar) { - child := node.Insert(ref, anyValue, values[0].Mapper) - for i := range values { - if values[i].Mapper != nil { - node.next.addMapper(values[i].Mapper) - } - } - node = child - } else { - // When a rule has multiple scalar values (e.g., internal.member_2 with a set), - // each value should have its own child node, and the rule is appended to each. - // This creates separate paths for each value so different rules with overlapping - // values don't interfere with each other. - for _, val := range values { - child := node.Insert(ref, val.Value, val.Mapper) - child.append(prio, rule) + } + remaining -= len(values) + + ref := table.ref(level) + + // A var value records "this ref can be anything", which a concrete value + // for the same ref supersedes: everything on one path has to hold, so the + // concrete value is the stronger of the two constraints. A chain of + // assignments, `x := input.a; y := x`, leaves one var entry per local + // behind, and only the first of them is replaced when the concrete value + // is inserted. Keeping the rest would index the rule under anyValue below + // and give up all the discrimination the concrete value buys us. + if len(values) > 1 { + if concrete := slices.DeleteFunc(slices.Clone(values), (*refindex).isVar); len(concrete) > 0 { + values = concrete + } + } + + if len(values) == 0 { + node = node.Insert(ref, nil, nil) + } else if len(values) == 1 { + node = values[0].insertInto(node, ref) + } else { + if slices.ContainsFunc(values, (*refindex).isVar) { + child := node.Insert(ref, anyValue, values[0].Mapper) + for i := range values { + if values[i].Mapper != nil { + node.next.addMapper(values[i].Mapper) } - return } + node = child + } else if remaining == 0 || slices.ContainsFunc(values, (*refindex).isAffix) || + slices.ContainsFunc(values, (*refindex).isComposite) { + // Nothing below to continue a path with, so the rule hangs off + // every alternative -- which rules reaching the same values + // share. Affixes always take this route; see alternation, and + // so does anything a converging level could not be keyed on: + // insertValue sends an object or a set to the "anything" node + // and an array into the array trie, which is where a lookup + // goes looking for them. + for _, val := range oneAffixEnd(values) { + child := val.insertInto(node, ref) + child.append(id, rule) + } + return + } else { + // The alternatives meet again on one node, and the rest of the + // path is built from there rather than under each of them. + node = node.insertAlternatives(ref, values) } } } - // Insert rule into trie with (insertion order, priority order) - // tuple. Retaining the insertion order allows us to return rules - // in the order they were passed to this function. - node.append(prio, rule) + node.append(id, rule) } func (i *baseDocEqIndex) Lookup(resolver ValueResolver) (*IndexResult, error) { tr := ttrPool.Get().(*trieTraversalResult) defer func() { - // Note(anderseknert): `clear`ing the map is not good enough here, as it'd mean - // resetting each of its slice values, costing us new allocations on each append - // in subsequent lookups - for i := range tr.unordered { - tr.unordered[i] = tr.unordered[i][:0] - } - tr.ordering = tr.ordering[:0] - tr.multiple = false - tr.exist = nil - + tr.reset() ttrPool.Put(tr) }() + tr.grow(len(i.rules)) err := i.root.Traverse(resolver, tr) if err != nil { return nil, err } - result := IndexResultPool.Get() result.Kind = i.kind result.Default = i.defaultRule result.OnlyGroundRefs = i.onlyGroundRefs - if result.Rules == nil { - result.Rules = make([]*Rule, 0, len(tr.ordering)) - } else { - result.Rules = result.Rules[:0] - } + result.Rules = result.Rules[:0] clear(result.Else) - for _, pos := range tr.ordering { - if len(tr.unordered[pos]) == 0 { + if err := i.gather(tr, resolver, result); err != nil { + IndexResultPool.Put(result) + return nil, err + } + + // Decided over the candidates rather than over what traversal saw, which is + // finer -- and has to be, now that the refs partition keeps out of the trie no + // longer separate the definitions into nodes of their own: of `p := 1 if + // input.foo`, `p := 2 if input.bar` and `p := 1 if input.baz`, the two agreeing + // on 1 are all a lookup returns when input has no bar. + tr.multiple = !resultMayEarlyExit(result) + + result.EarlyExit = !tr.multiple + + return result, nil +} + +// resultMayEarlyExit reports whether a caller could stop at the first candidate +// that holds, the else branches included: they are values it could stop at too. +func resultMayEarlyExit(result *IndexResult) bool { + var value Value + if !agreeOnValue(result.Rules, &value) { + return false + } + for _, branches := range result.Else { + if !agreeOnValue(branches, &value) { + return false + } + } + return true +} + +// agreeOnValue reports whether rules are complete documents agreeing on value, +// which it carries in so that several sets of rules can be asked as one. +func agreeOnValue(rules []*Rule, value *Value) bool { + for _, rule := range rules { + if rule.Head.DocKind() != CompleteDoc { + return false + } + if rule.Head.Value == nil { continue } + // A value that is not ground is a different one per binding, so there is + // no first answer to stop at. + v := rule.Head.Value.Value + if !v.IsGround() { + return false + } + if *value != nil && !ValueEqual(*value, v) { + return false + } + *value = v + } + return true +} - nodes := util.SortedFunc(tr.unordered[pos], (*ruleNode).prio1Cmp) - root := nodes[0].rule +// resolve answers for a reference, asking the resolver the first time only. +func (c *resolveCache) resolve(resolver ValueResolver, ref Ref) (Value, error) { + if c.keyOK && RefEqual(c.keyRef, ref) { + return c.keyVal, nil + } + v, err := resolver.Resolve(ref) + if err != nil { + return nil, err + } + c.keyRef, c.keyVal, c.keyOK = ref, v, true + return v, nil +} - result.Rules = append(result.Rules, root) - if len(nodes) > 1 { - if result.Else == nil { - result.Else = map[*Rule][]*Rule{} +// container resolves the collection at ref, resolving the container holding it +// once: `data.groups.g1.members` and `data.groups.g2.members` share one resolve. +func (c *resolveCache) container(resolver ValueResolver, ref Ref) (Value, error) { + const shared = 2 // data. + + if len(ref) <= shared { + return resolver.Resolve(ref) + } + + prefix := ref[:shared] + if !c.prefixOK || !RefEqual(c.prefix, prefix) { + v, err := resolver.Resolve(prefix) + if err != nil { + return nil, err + } + c.prefix, c.prefixVal, c.prefixOK = prefix, v, true + } + if c.prefixVal == nil { + return nil, nil + } + + v, err := c.prefixVal.Find(ref[shared:]) + if err != nil { + return nil, nil // undefined, not an error + } + return v, nil +} + +// consultsCollections reports whether excluding a candidate is worth consulting +// its collections for. It is not where the caller stops at the first candidate that +// holds: gather would have to consult all of them to exclude any, which is the work +// evaluation was about to do -- the lookup being what the rule tests. Whether a +// caller stops is the caller's own business, not IndexResult.EarlyExit's, which +// says what the ruleset permits, so the resolver is asked rather than assumed. +func (i *baseDocEqIndex) consultsCollections(resolver ValueResolver) bool { + if !i.mayEarlyExit { + return true + } + every, ok := resolver.(IndexEveryCandidateEvaluated) + return ok && every.IndexEveryCandidateEvaluated() +} + +// inCollections reports whether the rule's collection memberships hold. A +// collection the resolver cannot answer for keeps the rule, as does an array: +// the only thing to ask one is a position, which tells a ruleset's rules apart +// only where their lengths differ. +func (i *baseDocEqIndex) inCollections(resolver ValueResolver, id int32, cache *resolveCache) (bool, error) { + for _, m := range i.memberships[id] { + key, err := cache.resolve(resolver, m.key) + if err != nil { + if IsUnknownValueErr(err) { + continue } + return false, err + } + if key == nil { + return false, nil + } + + coll, err := cache.container(resolver, m.collection) + if err != nil { + if IsUnknownValueErr(err) { + continue + } + return false, err + } + if coll == nil { + return false, nil + } - result.Else[root] = make([]*Rule, len(nodes)-1) - for i := 1; i < len(nodes); i++ { - result.Else[root][i-1] = nodes[i].rule + probe := Term{Value: key} + switch c := coll.(type) { + case Object: + if c.Get(&probe) == nil { + return false, nil + } + // Base data read from JSON holds no set, but a store keeping ast.Value can, + // and so can a `with` statement replacing the collection. + case Set: + if !c.Contains(&probe) { + return false, nil } } } - if !tr.multiple { - // even when the indexer hasn't seen multiple values, the rule itself could be one - // where early exit shouldn't be applied. - var lastValue Value - for i := range result.Rules { - if result.Rules[i].Head.DocKind() != CompleteDoc { - tr.multiple = true - break + return true, nil +} + +// gather reads the rules a traversal reached into result. Ids ascend with +// priority, so a run of them sharing a group is that ruleset's definitions in +// order, the first being the one to evaluate. +// +// The refs partition kept out of the trie are checked here rather than as +// traversal reaches a rule: a bit set for a rule that turns out undefined costs +// nothing to leave set, and asking here means the resolver's error is the return +// value of something rather than a field to be picked up afterwards. A nil +// resolver asks nothing, which is what AllRules wants. +func (i *baseDocEqIndex) gather(tr *trieTraversalResult, resolver ValueResolver, result *IndexResult) error { + var cache resolveCache + var root *Rule + group := int32(-1) + consults := i.consultsCollections(resolver) + + // Words are marked as they are first written to, in traversal order. + slices.Sort(tr.touched) + + found := 0 + for _, w := range tr.touched { + found += bits.OnesCount64(tr.hits[w]) + } + result.Rules = slices.Grow(result.Rules, found) + + // A word holds 64 ids: `w<<6` is the id of its first bit, TrailingZeros64 the + // offset of the lowest set one, and `word &= word - 1` clears it. + for _, w := range tr.touched { + for word := tr.hits[w]; word != 0; word &= word - 1 { + id := w<<6 | int32(bits.TrailingZeros64(word)) + + if resolver != nil && len(i.required) > 0 { + defined, err := i.defined(resolver, id, &cache) + if err != nil { + return err + } + if !defined { + continue + } } - if result.Rules[i].Head.Value != nil { - if lastValue != nil && !ValueEqual(lastValue, result.Rules[i].Head.Value.Value) { - tr.multiple = true - break + + if consults && resolver != nil && len(i.memberships) > 0 { + in, err := i.inCollections(resolver, id, &cache) + if err != nil { + return err } - lastValue = result.Rules[i].Head.Value.Value + if !in { + continue + } + } + + rule := i.rules[id] + + if g := i.groups[id]; g != group { + group, root = g, rule + result.Rules = append(result.Rules, rule) + continue + } + + if result.Else == nil { + result.Else = map[*Rule][]*Rule{} } + result.Else[root] = append(result.Else[root], rule) } } - result.EarlyExit = !tr.multiple - - return result, nil + return nil } func (i *baseDocEqIndex) AllRules(ValueResolver) (*IndexResult, error) { tr := newTrieTraversalResult() + tr.grow(len(i.rules)) // Walk over the rule trie and accumulate _all_ rules rw := &ruleWalker{result: tr} @@ -266,27 +555,10 @@ func (i *baseDocEqIndex) AllRules(ValueResolver) (*IndexResult, error) { result := NewIndexResult(i.kind) result.Default = i.defaultRule result.OnlyGroundRefs = i.onlyGroundRefs - result.Rules = make([]*Rule, 0, len(tr.ordering)) - - for _, pos := range tr.ordering { - if len(tr.unordered[pos]) == 0 { - continue - } - slices.SortFunc(tr.unordered[pos], (*ruleNode).prio1Cmp) - nodes := tr.unordered[pos] - root := nodes[0].rule - result.Rules = append(result.Rules, root) - if len(nodes) > 1 { - if result.Else == nil { - result.Else = map[*Rule][]*Rule{} - } - result.Else[root] = make([]*Rule, len(nodes)-1) - for i := 1; i < len(nodes); i++ { - result.Else[root][i-1] = nodes[i].rule - } - } - } + // Every rule the trie holds, so nothing is asked of the resolver and + // nothing can fail; see gather. + _ = i.gather(tr, nil, result) result.EarlyExit = !tr.multiple @@ -308,10 +580,157 @@ type valueMapper struct { MapValue func(Value) Value } +// IndexEveryCandidateEvaluated may be implemented by a ValueResolver to answer +// whether every candidate a lookup returns goes on to be evaluated, rather than the +// caller stopping at the first that holds. Where it does, excluding a candidate +// saves evaluating it; see baseDocEqIndex.gather. +type IndexEveryCandidateEvaluated interface { + IndexEveryCandidateEvaluated() bool +} + +// membership is "the value at key has to be a key of the collection at +// collection", which a lookup asks the collection rather than the trie. +type membership struct{ key, collection Ref } + +// refID identifies one of the references an index is built on. +type refID int32 + +// refTable numbers the references an index is built on. One table is shared by +// every refindices of a build, the scratch ones an `and`/`or` operand is +// indexed into included, so that an id means the same thing wherever it turns +// up. +type refTable struct { + // refs are the references in id order; ids answers the other direction, and + // is only built past refTableScan entries. + refs []Ref + ids *util.HasherMap[Ref, refID] +} + +// refTableScan is how many references a table holds before it builds a map: +// below that, comparing a ref to the few already here beats hashing it, and most +// rulesets are indexed on a handful. +const refTableScan = 8 + +func newRefTable() *refTable { + return &refTable{} +} + +func (t *refTable) intern(ref Ref) refID { + if t.ids == nil { + for id, other := range t.refs { + if RefEqual(other, ref) { + return refID(id) + } + } + if len(t.refs) < refTableScan { + t.refs = append(t.refs, ref) + return refID(len(t.refs) - 1) + } + t.ids = util.NewHasherMap[Ref, refID](RefEqual) + for id, other := range t.refs { + t.ids.Put(other, refID(id)) + } + } + + if id, ok := t.ids.Get(ref); ok { + return id + } + id := refID(len(t.refs)) + t.refs = append(t.refs, ref) + t.ids.Put(ref, id) + return id +} + +func (t *refTable) ref(id refID) Ref { + return t.refs[id] +} + type refindex struct { - Ref Ref Value Value Mapper *valueMapper + // ref is the reference this constrains, as numbered by the build's table. + ref refID + // Affix says whether Value is a string the value at ref has to start or end + // with, rather than one it has to equal -- what startswith, endswith and + // their strings.any_*_match forms contribute. Several of them for one ref + // are alternatives, as for `in`. + Affix affix +} + +// affix is which end of the value at a reference a refindex constrains, if it +// constrains an end rather than the whole of it. +type affix uint8 + +const ( + affixNone affix = iota + affixPrefix + affixSuffix +) + +// insertInto adds the level this index constrains to the path being built, +// returning the node the rest of the path continues from. +func (i *refindex) insertInto(node *trieNode, ref Ref) *trieNode { + switch i.Affix { + case affixPrefix: + return node.InsertPrefix(ref, i.Value) + case affixSuffix: + return node.InsertSuffix(ref, i.Value) + } + return node.Insert(ref, i.Value, i.Mapper) +} + +// oneAffixEnd keeps the affixes of one end of the value where values constrain +// both, and everything that is not an affix. +// +// A rule hung off the leaves of both the prefix and the suffix trie is admitted +// by either, which is the disjunction of what it wrote where it wrote a +// conjunction: +// +// p if { +// strings.any_prefix_match(input.path, ["/a", "/b"]) +// strings.any_suffix_match(input.path, [".go", ".rego"]) +// } +// +// admits "/c/x.go" on the suffix alone. Testing one end and leaving the other to +// evaluation admits a subset of that -- what one end admits, both admit -- so +// one end is kept. A level cannot test both: the tries hold leaves, and a leaf +// cannot be made to depend on another trie's answer. +// +// Which end is kept is decided by the shortest base string of each, since a set +// admits a value that matches any one of its bases and the shortest of them +// admits the most. Counting them instead would keep ["/"] over [".go", +// ".rego"], and every absolute path matches "/". +func oneAffixEnd(values []*refindex) []*refindex { + prefix, suffix := -1, -1 + for _, val := range values { + s, ok := val.Value.(String) + if !ok { + continue + } + switch val.Affix { + case affixPrefix: + if prefix < 0 || len(s) < prefix { + prefix = len(s) + } + case affixSuffix: + if suffix < 0 || len(s) < suffix { + suffix = len(s) + } + } + } + + if prefix < 0 || suffix < 0 { + return values + } + + drop := affixSuffix + if suffix > prefix { + drop = affixPrefix + } + + return slices.DeleteFunc(slices.Clone(values), func(val *refindex) bool { + return val.Affix == drop + }) } // alternatives are sets of indices, any one of which is enough to reach a rule. @@ -325,9 +744,27 @@ type refindices struct { disjunctions map[*Rule][]alternatives // outer holds the enclosing scope's indices when this is the scratch for an // operand body: resolvable from inside, but not the operand's own. - outer []*refindex - frequency *util.HasherMap[Ref, int] - sorted []Ref + outer []*refindex + table *refTable + // memberships holds the collection memberships of each rule; see membership. + memberships map[*Rule][]membership + // stats holds what Sorted ranks the references by, indexed by ref id. + stats []refStats + sorted []refID +} + +// refStats is what one reference accumulated over a build, which is what decides +// the order of the trie's levels. Dropped once the trie is built. +type refStats struct { + // count is how often the ref took part in indexing a rule. Sorted passes + // over the ids that never counted: a scratch interns the refs of an operand + // that may turn out unindexable, and then nothing records them. + count int32 + // alternated is whether some rule reaches the ref by more than one value, + // and what that costs insertPath. An `or` is not recorded: its alternatives + // are separate paths, and only meet a second value for one ref once paths() + // combines them, after Sorted has run. + alternated alternation } // maxIndexPaths caps the ways a single rule may be reached: `or` expressions @@ -335,17 +772,43 @@ type refindices struct { // nodes cost more than evaluating the rule. const maxIndexPaths = 32 -func newrefindices(isVirtual func(Ref) bool) *refindices { +func newrefindices(isVirtual func(Ref) bool, table *refTable) *refindices { return &refindices{ - isVirtual: isVirtual, - rules: map[*Rule][]*refindex{}, - frequency: util.NewHasherMap[Ref, int](RefEqual), + isVirtual: isVirtual, + table: table, + rules: map[*Rule][]*refindex{}, + memberships: map[*Rule][]membership{}, + } +} + +// growTo extends s so that it can be indexed by every id below n, leaving what +// it already holds in place. +func growTo[T any](s []T, n int) []T { + if len(s) >= n { + return s } + return append(s, make([]T, n-len(s))...) +} + +func valueIsVar(v Value) bool { + _, ok := v.(Var) + return ok } func (i *refindex) isVar() bool { - _, isVar := i.Value.(Var) - return isVar + return valueIsVar(i.Value) +} + +func (i *refindex) isAffix() bool { + return i.Affix != affixNone +} + +// isComposite reports whether a lookup could not find this value among a +// level's alternatives, which are keyed on the value as it stands. insertValue +// sends an object or a set to the "anything" node and an array into the array +// trie, which is where a lookup goes looking for them instead. +func (i *refindex) isComposite() bool { + return !IsScalar(i.Value) } // Update attempts to update the refindices for the given expression in the @@ -380,8 +843,13 @@ func (i *refindices) Update(rule *Rule, expr *Expr, values map[Var]Value) { // check for type "Var" here. But since it's impossible to call a // function with a undefined argument, there's no point to recording // "needs to be anything" for function args - if _, ok := ts.Value.(Ref); ok { // "naked ref" - i.updateEq(rule, ts.Value, anyValue, nil) + if ref, ok := ts.Value.(Ref); ok { // "naked ref" + // `data.groups.g1.members[input.subject]` constrains its last + // element to the keys of the collection at the ground prefix, + // which is more than the "is defined" updateEq can record. + if !i.updateCollectionKey(rule, ref) { + i.updateEq(rule, ts.Value, anyValue, nil) + } } } } @@ -408,6 +876,20 @@ func (i *refindices) Update(rule *Rule, expr *Expr, values map[Var]Value) { case op.Equal(Interned.Refs.Member) && len(expr.Operands()) == 2: // NOTE(sr): Again, 3 operands means captured output (like above). i.updateMember(rule, expr, values) + + case op.Equal(Interned.Refs.StartsWith) && len(expr.Operands()) == 2: + // As with equal() above: a third operand captures the result, and a + // rule producing `false` still has to be evaluated. + i.updateAffix(rule, expr, values, affixPrefix) + + case op.Equal(Interned.Refs.AnyPrefixMatch) && len(expr.Operands()) == 2: + i.updateAnyAffixMatch(rule, expr, values, affixPrefix) + + case op.Equal(Interned.Refs.EndsWith) && len(expr.Operands()) == 2: + i.updateAffix(rule, expr, values, affixSuffix) + + case op.Equal(Interned.Refs.AnySuffixMatch) && len(expr.Operands()) == 2: + i.updateAnyAffixMatch(rule, expr, values, affixSuffix) } } @@ -440,7 +922,7 @@ func (i *refindices) require(rule *Rule, alts alternatives) { default: for _, alt := range alts { for _, ri := range alt { - i.count(ri.Ref) + i.count(ri.ref) } } if i.disjunctions == nil { @@ -473,7 +955,7 @@ func (i *refindices) updateLogicalOr(rule *Rule, or *LogicalOr, values map[Var]V // all means nothing about it could be indexed. It is indexed into a scratch, so // that what it requires reaches the rule only through require(). func (i *refindices) operandAlternatives(rule *Rule, body Body, values map[Var]Value) alternatives { - scratch := newrefindices(i.isVirtual) + scratch := newrefindices(i.isVirtual, i.table) scratch.outer = append(slices.Clone(i.rules[rule]), i.outer...) scratch.updateOperand(rule, body, values) @@ -488,7 +970,7 @@ func (i *refindices) operandAlternatives(rule *Rule, body Body, values map[Var]V // resolvable from the outside (see resolveVarToRef); that the ref // has to be defined still holds. if ri.isVar() { - alt[pos] = &refindex{Ref: ri.Ref, Value: anyValue, Mapper: ri.Mapper} + alt[pos] = &refindex{ref: ri.ref, Value: anyValue, Mapper: ri.Mapper} } } } @@ -543,37 +1025,98 @@ func (i *refindices) isValidIndexRef(ref Ref) bool { !i.isVirtual(ref) } -// Sorted returns a sorted list of references that the indices were built from. -// References that appear more frequently in the indexed rules are ordered -// before less frequently appearing references. -func (i *refindices) Sorted() []Ref { - if i.sorted == nil { - i.sorted = util.SortedFunc(i.frequency.Keys(), func(a, b Ref) int { - countsA, _ := i.frequency.Get(a) - countsB, _ := i.frequency.Get(b) - if countsA < countsB { // descending, we want highest-freq first - return 1 - } else if countsA > countsB { - return -1 - } - return a[0].Loc().Compare(b[0].Loc()) - }) +// Sorted returns the references the indices were built from, ordered so that +// the ones appearing in more of the indexed rules come first. +func (i *refindices) Sorted() []refID { + if i.sorted != nil { + return i.sorted } + + for id, stats := range i.stats { + if stats.count > 0 { + i.sorted = append(i.sorted, refID(id)) + } + } + + slices.SortFunc(i.sorted, func(a, b refID) int { + // A ref reached by several values is worth less as an early level, + // and one that ends the rule's path less again, however often + // either was recorded -- so both outrank frequency. + if c := cmp.Compare(i.stats[a].alternated, i.stats[b].alternated); c != 0 { + return c + } + if c := cmp.Compare(i.stats[b].count, i.stats[a].count); c != 0 { // descending + return c + } + if c := i.table.ref(a)[0].Loc().Compare(i.table.ref(b)[0].Loc()); c != 0 { + return c + } + // Refs built rather than parsed -- a function's args[n] -- share a + // location, so fall back on the order they were first seen in. + return cmp.Compare(a, b) + }) + return i.sorted } -func (i *refindices) Value(rule *Rule, ref Ref) Value { - if index := i.index(rule, ref); index != nil { - return index.Value +// partition splits sorted into the refs that become trie levels and the refs that +// do not, which is those no rule constrains to a value. +// +// A ref every rule records only as "holds something" -- which is what +// RewriteDynamicTerms leaves behind when it hoists a term into a local, +// `__local1__ = data.groups.g0.members` -- gives the trie a level whose only +// children are "anything" and "absent". It cannot narrow a lookup by value. What +// it can do is exclude the rules that read the ref when the ref is absent, since +// traversal stops at a level that resolves to nothing, and for a naked reference +// -- `allow if input.x` -- that is the whole of the indexing. +// +// Keeping it as a level is an expensive way to ask that question. Both children +// carry their own copy of the levels below, so the levels multiply out, and each +// lookup resolves a ref per copy. One such level per rule makes traversal +// quadratic: +// +// allow if { input.subject in data.groups.g0.members; input.resource.foo == "A" } +// allow if { input.subject in data.groups.g1.members; input.resource.foo == "A" } +// ... +// +// 500 of those resolve 125k refs on every lookup -- N(N+1)/2 -- to exclude nothing, +// because input.resource.foo is what discriminates. The index costs more than it +// saves there: the same policy evaluates 6.5x faster with indexing disabled, and +// 8.8x at a thousand rules. +// +// So the question moves out of the trie: Lookup checks these refs against the +// candidates traversal produced, which asks it once per surviving rule instead of +// once per copy of the level. The candidates are the same either way. +func (i *refindices) partition(sorted []refID) (levels, unvalued []refID) { + // An `or` records its operands' indices on disjunctions rather than through + // insert, so collect from both rather than counting as they arrive. + valued := make([]bool, len(i.stats)) + note := func(path []*refindex) { + for _, ri := range path { + if !ri.isVar() { + valued[ri.ref] = true + } + } + } + for _, path := range i.rules { + note(path) + } + for _, alts := range i.disjunctions { + for _, alt := range alts { + for _, path := range alt { + note(path) + } + } } - return nil -} -func (i *refindices) Mapper(rule *Rule, ref Ref) *valueMapper { - if index := i.index(rule, ref); index != nil { - return index.Mapper + for _, ref := range sorted { + if valued[ref] { + levels = append(levels, ref) + } else { + unvalued = append(unvalued, ref) + } } - return nil + return levels, unvalued } func (i *refindices) updateEq(rule *Rule, a, b Value, constants map[Var]Value) { @@ -594,6 +1137,11 @@ func (i *refindices) tryIndexWildcardRef(rule *Rule, a, b Value, constants map[V return false } + ref = i.resolveRefHead(rule, rule.Head.Args, ref) + if ref == nil { + return false + } + groundPrefix := ref.GroundPrefix() if len(groundPrefix) != len(ref)-1 || !i.isValidIndexRef(groundPrefix) { return false @@ -614,7 +1162,7 @@ func (i *refindices) tryIndexWildcardRef(rule *Rule, a, b Value, constants map[V return false } - i.insert(rule, &refindex{Ref: groundPrefix, Value: resolvedValue}) + i.insert(rule, &refindex{ref: i.table.intern(groundPrefix), Value: resolvedValue}) return true } @@ -632,9 +1180,9 @@ func (i *refindices) updateGlobMatch(rule *Rule, expr *Expr) { // variable earlier in the query OR a function argument variable. match := expr.Operand(2) if v, ok := match.Value.(Var); ok { - if ref := resolveVarToRef(i.resolvable(rule), args, v); ref != nil { + if ref := i.resolveVarToRef(i.resolvable(rule), args, v); ref != nil { i.insert(rule, &refindex{ - Ref: ref, + ref: i.table.intern(ref), Value: arr.Value, Mapper: &valueMapper{ Key: delim, @@ -655,7 +1203,7 @@ func (i *refindices) updateMember(rule *Rule, expr *Expr, constants map[Var]Valu lhs, rhs := expr.Operand(0), expr.Operand(1) lvar, ok := lhs.Value.(Var) if ok { - lref := resolveVarToRef(i.resolvable(rule), rule.Head.Args, lvar) + lref := i.resolveVarToRef(i.resolvable(rule), rule.Head.Args, lvar) if lref != nil { i.updateMemberRefInValue(rule, lref, rhs, constants) // `ref in value` return @@ -681,7 +1229,7 @@ func (i *refindices) updateMemberValueInRef(rule *Rule, args []*Term, lval Value return } - i.insert(rule, &refindex{Ref: rref, Value: lval}) + i.insert(rule, &refindex{ref: i.table.intern(rref), Value: lval}) } func (i *refindices) updateMemberRefInValue(rule *Rule, ref Ref, rhs *Term, constants map[Var]Value) { @@ -689,33 +1237,184 @@ func (i *refindices) updateMemberRefInValue(rule *Rule, ref Ref, rhs *Term, cons if rvar, ok := rval.(Var); ok { // rhs is var, try to resolve if resolved, ok := constants[rvar]; ok { rval = resolved + } else if cref := i.resolveVarToRef(i.resolvable(rule), rule.Head.Args, rvar); cref != nil { + // The collection is behind a reference the compiler hoisted into a + // local: `__local0__ = data.groups.g1.members` ahead of the call. + rval = cref } } - addRef := func(t *Term) error { - i.insert(rule, &refindex{Ref: ref, Value: t.Value}) - return nil + var ( + forEach func(func(*Term)) + n int + ) + + // `input.subject in data.groups.g1.members` asks for the collection's + // *values*, which base data -- an object or an array, never a set -- answers + // only by being walked. updateCollectionKey has the question it does answer. + if _, ok := rval.(Ref); ok { + return } switch rcol := rval.(type) { case *Array: - _ = rcol.Iter(addRef) + forEach, n = rcol.Foreach, rcol.Len() case Set: - _ = rcol.Iter(addRef) + forEach, n = rcol.Foreach, rcol.Len() case Object: - _ = rcol.Iter(func(_, v *Term) error { - return addRef(v) - }) + n = rcol.Len() + // Function literal does not escape / allocate + if o, ok := rcol.(*object); ok { + forEach = func(f func(*Term)) { + for _, node := range o.sortedKeys() { + f(node.value) + } + } + // Function literal escapes + } else { + forEach = func(f func(*Term)) { + rcol.Foreach(func(_, v *Term) { f(v) }) + } + } + default: + return + } + + members := make([]Value, 0, n) + forEach(func(t *Term) { + members = append(members, t.Value) + }) + + i.insertMembers(rule, ref, members) +} + +// insertMembers records the members of an `in` collection, each a value the +// rule may reach ref by, hoisting insert's scan out of the loop. insertAffixes +// is the same for base strings; the two dedup on different key types. +func (i *refindices) insertMembers(rule *Rule, ref Ref, members []Value) { + id := i.table.intern(ref) + + if len(members) < 2 { + for _, member := range members { + i.insert(rule, &refindex{ref: id, Value: member}) + } + return + } + + // Unlike a prefix, a concrete member takes the place of a "reference is + // anything" entry (see insert), so the first one goes the ordinary way -- + // the rule's list is short at that point, so the scan it costs is cheap. + i.insert(rule, &refindex{ref: id, Value: members[0]}) + + // insert is the only one that may put a value somewhere other than the end + // of the list, which is what a var needs, so those go in through it and are + // left out of the block below. A collection holding one is rare, and paying + // a copy for it keeps the common case a single pass. + rest := members[1:] + if slices.ContainsFunc(rest, valueIsVar) { + for _, member := range rest { + if valueIsVar(member) { + i.insert(rule, &refindex{ref: id, Value: member}) + } + } + rest = slices.DeleteFunc(slices.Clone(rest), valueIsVar) + } + + concrete := 0 + seen := util.NewHasherMap[Value, struct{}](ValueEqual) + + for _, other := range i.rules[rule] { + if other.ref != id { + continue + } + if !other.isVar() { + concrete++ + } + if other.Affix == affixNone { + seen.Put(other.Value, struct{}{}) + } + } + + // One refindex per member, laid down in a single block rather than + // allocated one at a time, as in insertAffixes. Duplicates leave slack at + // the end of the block, which the reslice drops. + pos := len(i.rules[rule]) + indices := util.GrowPtrSlice(i.rules[rule], len(rest)) + + for _, member := range rest { + if _, ok := seen.Get(member); ok { + continue + } + seen.Put(member, struct{}{}) + concrete++ + + *indices[pos] = refindex{ref: id, Value: member} + pos++ + } + i.rules[rule] = indices[:pos] + + i.countN(id, len(rest)) + + if concrete > 1 { + i.alternate(id, alternationConverging) } } +// updateCollectionKey records `[]` -- a reference whose ground +// prefix names a collection in base data and whose last element is the value +// being looked up in it. Reports whether it did. +func (i *refindices) updateCollectionKey(rule *Rule, ref Ref) bool { + if len(ref) < 2 || !ref[0].Equal(DefaultRootDocument) { + return false + } + + prefix := ref[:len(ref)-1] + if !prefix.IsGround() || i.isVirtual(prefix) { + return false + } + + keyRef := i.keyRefOf(rule, ref[len(ref)-1]) + if keyRef == nil || i.isVirtual(keyRef) { + return false + } + + i.recordMembership(rule, keyRef, prefix) + return true +} + +// keyRefOf resolves the term a collection is keyed by to the reference it stands +// for: `input.subject` directly, or the local a rule bound it to. +func (i *refindices) keyRefOf(rule *Rule, term *Term) Ref { + switch v := term.Value.(type) { + case Ref: + if v.IsGround() && !i.isVirtual(v) { + return v + } + case Var: + return i.resolveVarToRef(i.resolvable(rule), rule.Head.Args, v) + } + return nil +} + +// recordMembership notes that rule only matches when the value at key is a key +// of the collection at collection, and that both take part in indexing it. +func (i *refindices) recordMembership(rule *Rule, key, collection Ref) { + for _, m := range i.memberships[rule] { + if RefEqual(m.key, key) && RefEqual(m.collection, collection) { + return + } + } + i.memberships[rule] = append(i.memberships[rule], membership{key: key, collection: collection}) + i.count(i.table.intern(key)) +} + func (i *refindices) resolveAndValidateRef(rule *Rule, args []*Term, term *Term) Ref { var ref Ref switch v := term.Value.(type) { case Ref: ref = v case Var: - ref = resolveVarToRef(i.resolvable(rule), args, v) + ref = i.resolveVarToRef(i.resolvable(rule), args, v) default: return nil } @@ -727,6 +1426,28 @@ func (i *refindices) resolveAndValidateRef(rule *Rule, args []*Term, term *Term) return ref } +// resolveRefHead resolves a ref rooted at a local variable -- what +// +// x := input +// x.foo == "bar" +// +// gets compiled to -- into the ref that local aliases, splicing the remainder of +// the ref onto it: `input.foo`. Refs that are already rooted at a root document +// are returned unchanged; a head that does not resolve yields nil. +func (i *refindices) resolveRefHead(rule *Rule, args []*Term, ref Ref) Ref { + head, isVar := ref[0].Value.(Var) + if !isVar || RootDocumentNames.Contains(ref[0]) { + return ref + } + + resolved := i.resolveVarToRef(i.resolvable(rule), args, head) + if resolved == nil { + return nil + } + + return resolved.Concat(ref[1:]) +} + // resolveVarToRef checks the previously prepared `*refindex` slice for // occurrences of the var `v`. Since we store `ref = var` expressions for // "any" lookups (i.e. "return the rule if ref is anything"), we can @@ -748,10 +1469,10 @@ func (i *refindices) resolveAndValidateRef(rule *Rule, args []*Term, term *Term) // // // as we're not capturing `var = var` expressions in the index. -func resolveVarToRef(ri []*refindex, args []*Term, v Var) Ref { +func (i *refindices) resolveVarToRef(ri []*refindex, args []*Term, v Var) Ref { for _, other := range ri { if v.Equal(other.Value) { - return other.Ref + return i.table.ref(other.ref) } } for j, arg := range args { @@ -774,50 +1495,187 @@ func (i *refindices) resolvable(rule *Rule) []*refindex { // count records that ref took part in indexing a rule, which is what orders the // trie levels (see Sorted). -func (i *refindices) count(ref Ref) { - count, _ := i.frequency.Get(ref) - i.frequency.Put(ref, count+1) +func (i *refindices) count(ref refID) { + i.countN(ref, 1) } -func (i *refindices) insert(rule *Rule, index *refindex) { - i.count(index.Ref) +func (i *refindices) countN(ref refID, n int) { + i.stat(ref).count += int32(n) +} + +// stat returns the reference's statistics, making room for them if this is the +// first thing recorded about it. +func (i *refindices) stat(ref refID) *refStats { + i.stats = growTo(i.stats, int(ref)+1) + return &i.stats[ref] +} + +// alternation is what a ref reached by several values costs the rest of the +// rule's path, and what Sorted ranks such refs by. +type alternation uint8 + +const ( + // alternationNone: no rule reaches the ref by more than one value. + alternationNone alternation = iota + + // alternationConverging: the alternatives meet again on one node, so the + // path continues from there. Still ranked after the plain refs, since the + // rule gets a node of its own and stops sharing what is below. + alternationConverging - _, indexValueIsVar := index.Value.(Var) + // alternationTerminal: the alternatives cannot meet again, so the rule + // hangs off each and whatever it constrains below goes unindexed. Affixes + // are these -- a prefix trie cannot point several leaves at one node. + alternationTerminal +) + +// alternate records that a rule reaches ref by more than one value, and what +// that costs. The worse kind recorded for a ref wins. Only the values +// surviving insertPath's var-stripping count. +func (i *refindices) alternate(ref refID, kind alternation) { + if kind == alternationNone { + return + } + + stats := i.stat(ref) + stats.alternated = max(stats.alternated, kind) +} + +func (i *refindices) insert(rule *Rule, index *refindex) { + indexValueIsVar := index.isVar() + seen := false for pos, other := range i.rules[rule] { - if other.Ref.Equal(index.Ref) { - if ValueEqual(other.Value, index.Value) { + if other.ref == index.ref { + seen = true + if other.Affix == index.Affix && ValueEqual(other.Value, index.Value) { return } - _, otherValueIsVar := other.Value.(Var) - if !indexValueIsVar && otherValueIsVar { + otherValueIsVar := other.isVar() + // An affix constraint does not take the place of the "ref is + // anything" entry the way a concrete value does: that entry is what + // lets a later expression resolve the same local back to this ref + // (see resolveVarToRef), and insertPath drops it anyway once the + // ref has a concrete value on the path. + if !indexValueIsVar && index.Affix == affixNone && otherValueIsVar { i.rules[rule][pos] = index return } + if !indexValueIsVar && !otherValueIsVar { + // insertPath cannot converge a level that any affix reaches, + // so one on either side makes this pair a terminal one. + kind := alternationConverging + if index.Affix != affixNone || other.Affix != affixNone { + kind = alternationTerminal + } + i.alternate(index.ref, kind) + } } } - i.rules[rule] = append(i.rules[rule], index) -} - -func (i *refindices) index(rule *Rule, ref Ref) *refindex { - for _, index := range i.rules[rule] { - if index.Ref.Equal(ref) { - return index - } + if !seen { + i.count(index.ref) } - return nil + i.rules[rule] = append(i.rules[rule], index) } type trieWalker interface { Do(any) trieWalker } +// trieTraversalResult is what a walk of the trie -- a lookup, or the whole of it +// for AllRules -- collects. +// +// The rules reached are a bitset over the index's rule ids, so reaching one down +// several paths costs nothing to notice: the second arrival writes a bit that is +// already set. Reading it back in id order is reading it grouped and in priority +// order, ids having been handed out that way, so there is nothing left to sort. type trieTraversalResult struct { - unordered map[int][]*ruleNode - ordering []int - exist *Term - multiple bool + hits []uint64 + // touched holds the words of hits that were written to, so that clearing + // costs what a lookup found rather than what the index holds. + touched []int32 + exist *Term + multiple bool +} + +// defined reports whether every ref the rule needs resolves to something. A ref +// that is unknown rather than absent cannot exclude it, the same way traversal +// keeps everything below a level it cannot resolve (see traverseUnknown). +func (i *baseDocEqIndex) defined(resolver ValueResolver, id int32, cache *resolveCache) (bool, error) { + for _, pos := range i.required[id] { + defined, err := cache.defined(resolver, i.requiredRefs, pos) + if err != nil { + return false, err + } + if !defined { + return false, nil + } + } + + return true, nil +} + +// resolveCache memoizes, for the length of one lookup, what the resolver answered. +// The refs kept out of the trie are asked for once each however many rules read +// them, and the collections of a ruleset sit under a shared prefix. +// +// Where topdown's baseCache holds what the store gave, making a resolve cheap, this +// skips making the call. +type resolveCache struct { + // required is one entry per ref in the index's requiredRefs, so a memo is + // read at a position rather than searched for. It used to be one entry per + // ref asked about, found by scanning: refs no rule constrains to a value are + // usually the same few, but a rule reading one of its own gives a lookup as + // many distinct ones as there are candidates, and the scan then costs a + // comparison per pair of them. + required []resolved + // keyRef and prefix are the last key and the last collection container asked + // for: a ruleset's rules test the same field, and their collections sit side + // by side under one prefix. + keyRef Ref + keyVal Value + keyOK bool + prefix Ref + prefixVal Value + prefixOK bool +} + +// resolved is a memo entry: unasked until a lookup asks, and then one of the two +// answers. Zero has to mean unasked, so that the buffer needs no initialising +// beyond being allocated. +type resolved uint8 + +const ( + unasked resolved = iota + isDefined + isAbsent +) + +func (c *resolveCache) defined(resolver ValueResolver, refs []Ref, pos int32) (bool, error) { + if c.required == nil { + c.required = make([]resolved, len(refs)) + } + if r := c.required[pos]; r != unasked { + return r == isDefined, nil + } + + v, err := resolver.Resolve(refs[pos]) + if err != nil { + if !IsUnknownValueErr(err) { + return false, err + } + // Unknown rather than absent cannot exclude the rule, the same way + // traversal keeps everything below a level it cannot resolve. + v = Boolean(true) + } + + if v != nil { + c.required[pos] = isDefined + return true, nil + } + c.required[pos] = isAbsent + return false, nil } var ttrPool = &sync.Pool{ @@ -827,20 +1685,34 @@ var ttrPool = &sync.Pool{ } func newTrieTraversalResult() *trieTraversalResult { - return &trieTraversalResult{ - unordered: make(map[int][]*ruleNode, 16), + return &trieTraversalResult{} +} + +// grow makes room for an index holding n rules. The pool never sizes back down, +// which is a byte per eight rules against an index costing hundreds per rule. +func (tr *trieTraversalResult) grow(n int) { + tr.hits = growTo(tr.hits, (n+63)/64) +} + +func (tr *trieTraversalResult) reset() { + for _, w := range tr.touched { + tr.hits[w] = 0 } + tr.touched = tr.touched[:0] + tr.multiple = false + tr.exist = nil } func (tr *trieTraversalResult) Add(t *trieNode) { - for _, node := range t.rules { - root := node.prio[0] - if nodes, ok := tr.unordered[root]; !ok || len(nodes) == 0 { - tr.ordering = append(tr.ordering, root) - tr.unordered[root] = append(nodes, node) - } else if !slices.ContainsFunc(nodes, node.prioEqual) { - tr.unordered[root] = append(nodes, node) + for _, id := range t.rules { + word, bit := id>>6, uint64(1)<<(uint(id)&63) + if tr.hits[word]&bit != 0 { + continue + } + if tr.hits[word] == 0 { + tr.touched = append(tr.touched, word) } + tr.hits[word] |= bit } if t.multiple { tr.multiple = true @@ -856,20 +1728,17 @@ func (tr *trieTraversalResult) Add(t *trieNode) { } type trieNode struct { - ref Ref - mappers []*valueMapper - next *trieNode - any *trieNode - undefined *trieNode - scalars *util.HasherMap[Value, *trieNode] - array *trieNode - rules []*ruleNode - value *Term - multiple bool + // next is the level below this node, nil where the paths under it end. + next *levelDetail + // rules are the ids of the rules whose path ends here, see + // baseDocEqIndex.rules. + rules []int32 + value *Term + multiple bool } -func (node *trieNode) append(prio [2]int, rule *Rule) { - node.rules = append(node.rules, &ruleNode{prio, rule}) +func (node *trieNode) append(id int32, rule *Rule) { + node.rules = append(node.rules, id) if node.value != nil && rule.Head.Value != nil && !node.value.Equal(rule.Head.Value) { node.multiple = true @@ -880,21 +1749,101 @@ func (node *trieNode) append(prio [2]int, rule *Rule) { } } -type ruleNode struct { - prio [2]int - rule *Rule +// levelDetail is everything a trieNode has by virtue of being a *level* -- the +// reference it resolves, the children it dispatches the resolved value to, and +// the constraints that are not exact values. The suffix trie holds its base +// strings reversed, so that requiring one at the end of a value is requiring it +// at the start of the value reversed and the same trie answers both (see +// traverseSuffix). +// +// It is held behind one pointer because a trieNode is allocated per indexed +// value and almost none of them are levels: half a million prefixes make one +// level and half a million nodes that only carry rules. Measured over such an +// index, every field here is set on 0 or 1 of the 500002 nodes. +// +// Where the boundaries fall decides how much that is worth. Inline, these +// fields put trieNode in Go's 160-byte size class; out of line it is 56 bytes, +// which rounds to 64. Moving them out a few at a time buys nothing -- 136 and +// 112 bytes both round up to a class the struct already occupied. +// +// The same reasoning applies once more within levelDetail: alternatives is set +// on the few levels some rule reaches by more than one value, so it costs 8 +// bytes here rather than the 32 its two fields would inline. +type levelDetail struct { + ref Ref + any *trieNode + undefined *trieNode + array *arrayTrie + scalars *util.HasherMap[Value, *trieNode] + mappers []*valueMapper + prefixes *prefixTrie + suffixes *prefixTrie + alternatives *alternativeChildren +} + +// alternativeChildren are the nodes that rules reaching a level by several +// values continue from. The two fields hold the same nodes for two different +// jobs, and neither does the other's: +// +// members answers "which nodes does this value reach", which is what a lookup +// asks. A node is in it under every one of the values that reaches it, so a +// rule with a thousand-member collection puts its one node under a thousand +// keys, and several rules sharing a value put several nodes under that one. +// +// converged answers "which nodes are below this level", which is what the +// walks over the whole trie ask -- traverseUnknown, Do and compact. Reading +// that off members would visit a node once per value that reaches it: correct, +// since trieTraversalResult.Add folds a rule reached twice into one, but a +// thousand times the work for the collection above. So the nodes are listed +// once each here as they are created. +type alternativeChildren struct { + members *util.HasherMap[Value, []*trieNode] + converged []*trieNode +} + +func newTrieNodeImpl() *trieNode { + return &trieNode{} +} + +// level returns the level below node, creating it if this is the first rule to +// be discriminated there. +func (node *trieNode) level() *levelDetail { + node.next = util.Or(node.next, newLevelDetail) + return node.next +} + +func (d *levelDetail) converged() []*trieNode { + if d != nil && d.alternatives != nil { + return d.alternatives.converged + } + return nil +} + +// affixTrie returns the trie for one end of the value, creating it and the +// detail that holds it on first use. +func (d *levelDetail) affixTrie(a affix) *prefixTrie { + detail := d + + switch a { + case affixSuffix: + detail.suffixes = util.Or(detail.suffixes, newPrefixTrie) + return detail.suffixes + default: + detail.prefixes = util.Or(detail.prefixes, newPrefixTrie) + return detail.prefixes + } } -func (a *ruleNode) prio1Cmp(b *ruleNode) int { - return a.prio[1] - b.prio[1] +func newLevelDetail() *levelDetail { + return &levelDetail{} } -func (a *ruleNode) prioEqual(b *ruleNode) bool { - return a.prio == b.prio +func newScalarChildren() *util.HasherMap[Value, *trieNode] { + return util.NewHasherMap[Value, *trieNode](ValueEqual) } -func newTrieNodeImpl() *trieNode { - return &trieNode{} +func newPrefixTrie() *prefixTrie { + return &prefixTrie{} } func (node *trieNode) Do(walker trieWalker) { @@ -906,29 +1855,106 @@ func (node *trieNode) Do(walker trieWalker) { return } - node.any.Do(next) - node.undefined.Do(next) + node.next.do(next) +} - node.scalars.Iter(func(_ Value, child *trieNode) bool { - child.Do(next) +func (d *levelDetail) do(walker trieWalker) { + if d == nil { + return + } + + d.any.Do(walker) + d.undefined.Do(walker) + + d.scalars.Iter(func(_ Value, child *trieNode) bool { + child.Do(walker) return false }) - node.array.Do(next) - node.next.Do(next) + for _, child := range d.converged() { + child.Do(walker) + } + + d.prefixes.do(walker) + d.suffixes.do(walker) + d.array.do(walker) } -func (node *trieNode) Insert(ref Ref, value Value, mapper *valueMapper) *trieNode { - if node.next == nil { - node.next = newTrieNodeImpl() - node.next.ref = ref +// compact walks the trie once the index is built and releases what its slices +// grew but do not use. +func (node *trieNode) compact() { + if node == nil { + return + } + + node.next.compact() +} + +func (d *levelDetail) compact() { + if d == nil { + return + } + + d.prefixes.compact() + d.suffixes.compact() + + d.any.compact() + d.undefined.compact() + d.array.compact() + + for _, child := range d.converged() { + child.compact() + } + + d.scalars.Iter(func(_ Value, child *trieNode) bool { + child.compact() + return false + }) + + if d.alternatives != nil { + d.alternatives.converged = slices.Clip(d.alternatives.converged) } +} + +// insertAlternatives adds a level a rule reaches by any one of several values, +// and returns the one node the rest of its path continues from. Every value +// keys to that node, so what the rule constrains below is built once instead of +// repeated under each alternative. +func (node *trieNode) insertAlternatives(ref Ref, values []*refindex) *trieNode { + level := node.level() + level.ref = ref + level.alternatives = util.Or(level.alternatives, newAlternativeChildren) + alt := level.alternatives + + converge := newTrieNodeImpl() + alt.converged = append(alt.converged, converge) + + for _, val := range values { + if val.Mapper != nil { + level.addMapper(val.Mapper) + } + nodes, _ := alt.members.Get(val.Value) + alt.members.Put(val.Value, append(nodes, converge)) + } + + return converge +} + +func newAlternativeChildren() *alternativeChildren { + return &alternativeChildren{ + members: util.NewHasherMap[Value, []*trieNode](ValueEqual), + } +} + +func (node *trieNode) Insert(ref Ref, value Value, mapper *valueMapper) *trieNode { + level := node.level() + level.ref = ref if mapper != nil { - node.next.addMapper(mapper) + level.addMapper(mapper) } - return node.next.insertValue(value) + return level.insertValue(value) } func (node *trieNode) Traverse(resolver ValueResolver, tr *trieTraversalResult) error { @@ -941,36 +1967,37 @@ func (node *trieNode) Traverse(resolver ValueResolver, tr *trieTraversalResult) return node.next.traverse(resolver, tr) } -func (node *trieNode) addMapper(mapper *valueMapper) { - for i := range node.mappers { - if node.mappers[i].Key == mapper.Key { +func (d *levelDetail) addMapper(mapper *valueMapper) { + detail := d + for i := range detail.mappers { + if detail.mappers[i].Key == mapper.Key { return } } - node.mappers = append(node.mappers, mapper) + detail.mappers = append(detail.mappers, mapper) } -func (node *trieNode) insertValue(value Value) *trieNode { +func (d *levelDetail) insertValue(value Value) *trieNode { + detail := d + switch value := value.(type) { case nil: - node.undefined = util.Or(node.undefined, newTrieNodeImpl) - return node.undefined + detail.undefined = util.Or(detail.undefined, newTrieNodeImpl) + return detail.undefined case Var: - node.any = util.Or(node.any, newTrieNodeImpl) - return node.any + detail.any = util.Or(detail.any, newTrieNodeImpl) + return detail.any case Null, Boolean, Number, String: - child, ok := node.scalars.Get(value) + child, ok := detail.scalars.Get(value) if !ok { child = newTrieNodeImpl() - if node.scalars == nil { - node.scalars = util.NewHasherMap[Value, *trieNode](ValueEqual) - } - node.scalars.Put(value, child) + detail.scalars = util.Or(detail.scalars, newScalarChildren) + detail.scalars.Put(value, child) } return child case *Array: - node.array = util.Or(node.array, newTrieNodeImpl) - return node.array.insertArray(value) + detail.array = util.Or(detail.array, newArrayTrie) + return detail.array.insert(value) // `x in ` (see updateMemberRefInValue) inserts each element of // the literal collection as-is, without restricting it to scalars/arrays @@ -981,59 +2008,147 @@ func (node *trieNode) insertValue(value Value) *trieNode { // Call - can't actually reach here: the compiler rewrites them into // separate statements, bound to a Var, before the index is built.) case Object, Set: - node.any = util.Or(node.any, newTrieNodeImpl) - return node.any + detail.any = util.Or(detail.any, newTrieNodeImpl) + return detail.any } panic("illegal value") } -func (node *trieNode) insertArray(arr *Array) *trieNode { +// arrayTrie dispatches on the elements of an array, one node per position. A +// position dispatches the element after it and ends a rule's array, which a +// trieNode cannot hold at once. +type arrayTrie struct { + any *arrayTrie + scalars *util.HasherMap[Value, *arrayTrie] + // end is where a rule whose array ends at this position continues. + end *trieNode +} + +func newArrayTrie() *arrayTrie { + return &arrayTrie{} +} + +func newArrayChildren() *util.HasherMap[Value, *arrayTrie] { + return util.NewHasherMap[Value, *arrayTrie](ValueEqual) +} + +// insert returns the node the rule's path continues from once arr is consumed. +func (a *arrayTrie) insert(arr *Array) *trieNode { if arr.Len() == 0 { - return node + a.end = util.Or(a.end, newTrieNodeImpl) + return a.end } switch head := arr.Elem(0).Value.(type) { - case Var: - node.any = util.Or(node.any, newTrieNodeImpl) - return node.any.insertArray(arr.Slice(1, -1)) case Null, Boolean, Number, String: - child, ok := node.scalars.Get(head) + child, ok := a.scalars.Get(head) if !ok { - child = newTrieNodeImpl() - if node.scalars == nil { - node.scalars = util.NewHasherMap[Value, *trieNode](ValueEqual) - } - node.scalars.Put(head, child) + child = newArrayTrie() + a.scalars = util.Or(a.scalars, newArrayChildren) + a.scalars.Put(head, child) } - return child.insertArray(arr.Slice(1, -1)) + return child.insert(arr.Slice(1, -1)) - // Same reasoning as in insertValue above: an array element can itself be - // a nested array, object, or set, none of which can be indexed precisely - // at this position, so fall back to "any" and keep indexing the - // remaining elements. - case *Array, Object, Set: - node.any = util.Or(node.any, newTrieNodeImpl) - return node.any.insertArray(arr.Slice(1, -1)) + // An element that is itself an array, object or set cannot be indexed + // precisely at this position, so -- as for a var -- it falls back to any, + // and the elements after it go on being indexed. + case Var, *Array, Object, Set: + a.any = util.Or(a.any, newArrayTrie) + return a.any.insert(arr.Slice(1, -1)) } panic("illegal value") } -func (node *trieNode) traverse(resolver ValueResolver, tr *trieTraversalResult) error { - if node == nil { +func (a *arrayTrie) traverse(resolver ValueResolver, tr *trieTraversalResult, arr *Array) error { + if a == nil { + return nil + } + + if arr.Len() == 0 { + return a.end.Traverse(resolver, tr) + } + + if err := a.any.traverse(resolver, tr, arr.Slice(1, -1)); err != nil { + return err + } + + switch head := arr.Elem(0).Value.(type) { + case Null, Boolean, Number, String: + child, _ := a.scalars.Get(head) + return child.traverse(resolver, tr, arr.Slice(1, -1)) + } + + return nil +} + +func (a *arrayTrie) traverseUnknown(resolver ValueResolver, tr *trieTraversalResult) error { + if a == nil { return nil } - v, err := resolver.Resolve(node.ref) + if err := a.end.Traverse(resolver, tr); err != nil { + return err + } + + if err := a.any.traverseUnknown(resolver, tr); err != nil { + return err + } + + var iterErr error + a.scalars.Iter(func(_ Value, child *arrayTrie) bool { + iterErr = child.traverseUnknown(resolver, tr) + return iterErr != nil + }) + + return iterErr +} + +func (a *arrayTrie) do(walker trieWalker) { + if a == nil { + return + } + + a.end.Do(walker) + a.any.do(walker) + a.scalars.Iter(func(_ Value, child *arrayTrie) bool { + child.do(walker) + return false + }) +} + +func (a *arrayTrie) compact() { + if a == nil { + return + } + + a.end.compact() + a.any.compact() + a.scalars.Iter(func(_ Value, child *arrayTrie) bool { + child.compact() + return false + }) +} + +func (d *levelDetail) traverse(resolver ValueResolver, tr *trieTraversalResult) error { + if d == nil { + return nil + } + + v, err := resolver.Resolve(d.ref) if err != nil { if IsUnknownValueErr(err) { - return node.traverseUnknown(resolver, tr) + return d.traverseUnknown(resolver, tr) } return err } - if err = node.undefined.Traverse(resolver, tr); err != nil { + // Which order the branches below are taken in does not decide the order the + // candidates come back in -- gather reads them by id. Only undefined coming + // before the nil return is load-bearing: a ref that resolved to nothing + // admits the rules wanting it undefined and no others. + if err = d.undefined.Traverse(resolver, tr); err != nil { return err } @@ -1041,18 +2156,30 @@ func (node *trieNode) traverse(resolver ValueResolver, tr *trieTraversalResult) return nil } - if err = node.any.Traverse(resolver, tr); err != nil { + if err = d.any.Traverse(resolver, tr); err != nil { + return err + } + + if err = d.traverseValue(resolver, tr, v); err != nil { + return err + } + + // Prefix constraints are tested against the value as it is, never against + // what a mapper makes of it: the glob mapper turns a string into the array + // of its segments, and matching prefixes against those segments would + // answer a question no rule asked. + if err = d.traversePrefixes(resolver, tr, v); err != nil { return err } - if err = node.traverseValue(resolver, tr, v); err != nil { + if err = d.traverseSuffixes(resolver, tr, v); err != nil { return err } - for i := range node.mappers { - mapped := node.mappers[i].MapValue(v) + for i := range d.mappers { + mapped := d.mappers[i].MapValue(v) if !ValueEqual(mapped, v) { - if err := node.traverseValue(resolver, tr, mapped); err != nil { + if err := d.traverseValue(resolver, tr, mapped); err != nil { return err } } @@ -1061,33 +2188,67 @@ func (node *trieNode) traverse(resolver ValueResolver, tr *trieTraversalResult) return nil } -func (node *trieNode) traverseValue(resolver ValueResolver, tr *trieTraversalResult, value Value) error { +func (d *levelDetail) traverseValue(resolver ValueResolver, tr *trieTraversalResult, value Value) error { switch value := value.(type) { case *Array, Set, Object: - if node.array != nil { + if d.array != nil { if arr, ok := value.(*Array); ok { - if err := node.array.traverseArray(resolver, tr, arr); err != nil { + if err := d.array.traverse(resolver, tr, arr); err != nil { return err } } } - if node.scalars.Len() > 0 { - return node.traverseCollectionMembership(resolver, tr, value) + // Alternatives as well as scalars: a level every rule reaches by + // several values has its children under alternatives and none under + // scalars, and a collection at the reference still has to be tested + // against them. + if d.scalars.Len() > 0 || d.alternatives != nil { + return d.traverseCollectionMembership(resolver, tr, value) } case Null, Boolean, Number, String: - if child, ok := node.scalars.Get(value); ok { - return child.Traverse(resolver, tr) + if child, ok := d.scalars.Get(value); ok { + if err := child.Traverse(resolver, tr); err != nil { + return err + } + } + // A level with no alternatives -- almost all of them -- pays a branch + // and nothing more. + if d.alternatives != nil { + return d.alternatives.traverse(resolver, tr, value) } } return nil } -func (node *trieNode) traverseCollectionMembership(resolver ValueResolver, tr *trieTraversalResult, collection Value) error { +// traverse visits the nodes that the rules reaching this level by value +// continue from. +func (alt *alternativeChildren) traverse(resolver ValueResolver, tr *trieTraversalResult, value Value) error { + nodes, ok := alt.members.Get(value) + if !ok { + return nil + } + + for _, child := range nodes { + if err := child.Traverse(resolver, tr); err != nil { + return err + } + } + + return nil +} + +func (d *levelDetail) traverseCollectionMembership(resolver ValueResolver, tr *trieTraversalResult, collection Value) error { + alt := d.alternatives checkMember := func(t *Term) error { if IsScalar(t.Value) { - child, _ := node.scalars.Get(t.Value) - return child.Traverse(resolver, tr) + child, _ := d.scalars.Get(t.Value) + if err := child.Traverse(resolver, tr); err != nil { + return err + } + if alt != nil { + return alt.traverse(resolver, tr, t.Value) + } } return nil } @@ -1098,6 +2259,13 @@ func (node *trieNode) traverseCollectionMembership(resolver ValueResolver, tr *t case Set: return col.Iter(checkMember) case Object: + // Function literal does not escape + if o, ok := col.(*object); ok { + return o.Iter(func(_, v *Term) error { + return checkMember(v) + }) + } + // Function literal escapes return col.Iter(func(_, v *Term) error { return checkMember(v) }) @@ -1106,50 +2274,44 @@ func (node *trieNode) traverseCollectionMembership(resolver ValueResolver, tr *t return nil } -func (node *trieNode) traverseArray(resolver ValueResolver, tr *trieTraversalResult, arr *Array) (err error) { - if node == nil { +// traverseUnknown visits every child of a level whose reference the resolver +// cannot answer for. What each child constrains below resolves as usual: an +// unknown at one level says nothing about the levels under it. +func (d *levelDetail) traverseUnknown(resolver ValueResolver, tr *trieTraversalResult) error { + if d == nil { return nil } - if arr.Len() == 0 { - return node.Traverse(resolver, tr) - } - - if err = node.any.traverseArray(resolver, tr, arr.Slice(1, -1)); err == nil { - switch head := arr.Elem(0).Value.(type) { - case Null, Boolean, Number, String: - child, _ := node.scalars.Get(head) - return child.traverseArray(resolver, tr, arr.Slice(1, -1)) - } + if err := d.undefined.Traverse(resolver, tr); err != nil { + return err } - return err -} - -func (node *trieNode) traverseUnknown(resolver ValueResolver, tr *trieTraversalResult) error { - if node == nil { - return nil + if err := d.any.Traverse(resolver, tr); err != nil { + return err } - if err := node.Traverse(resolver, tr); err != nil { + if err := d.array.traverseUnknown(resolver, tr); err != nil { return err } - if err := node.undefined.traverseUnknown(resolver, tr); err != nil { + if err := d.prefixes.traverseUnknown(resolver, tr); err != nil { return err } - if err := node.any.traverseUnknown(resolver, tr); err != nil { + if err := d.suffixes.traverseUnknown(resolver, tr); err != nil { return err } - if err := node.array.traverseUnknown(resolver, tr); err != nil { - return err + for _, child := range d.converged() { + if err := child.Traverse(resolver, tr); err != nil { + return err + } } var iterErr error - node.scalars.Iter(func(_ Value, child *trieNode) bool { - return child.traverseUnknown(resolver, tr) != nil + d.scalars.Iter(func(_ Value, child *trieNode) bool { + iterErr = child.Traverse(resolver, tr) + return iterErr != nil }) return iterErr @@ -1171,13 +2333,16 @@ func (i *refindices) eqOperandsToRefAndValue(rule *Rule, args []*Term, a, b Valu if !ok { return false } - if ref := resolveVarToRef(i.resolvable(rule), args, v); ref != nil { - i.insert(rule, &refindex{Ref: ref, Value: bval}) + if ref := i.resolveVarToRef(i.resolvable(rule), args, v); ref != nil { + i.insert(rule, &refindex{ref: i.table.intern(ref), Value: bval}) return true } case Ref: - if !i.isValidIndexRef(v) { + // A ref rooted at a local -- `x := input; x.foo == "bar"` -- indexes the + // same as the ref that local aliases, so long as the local resolves. + v = i.resolveRefHead(rule, args, v) + if v == nil || !i.isValidIndexRef(v) { return false } @@ -1191,7 +2356,7 @@ func (i *refindices) eqOperandsToRefAndValue(rule *Rule, args []*Term, a, b Valu return false } - i.insert(rule, &refindex{Ref: v, Value: b}) + i.insert(rule, &refindex{ref: i.table.intern(v), Value: b}) return true } return false diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/index_affix.go b/vendor/github.com/open-policy-agent/opa/v1/ast/index_affix.go new file mode 100644 index 0000000000..9288428646 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/index_affix.go @@ -0,0 +1,594 @@ +// Copyright 2026 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "slices" + + "github.com/open-policy-agent/opa/v1/util" +) + +// This file holds the indexing of both ends of a string: `startswith` and +// `strings.any_prefix_match`, and `endswith` and `strings.any_suffix_match`. +// One structure answers both -- a suffix trie is a prefixTrie over the base +// strings reversed (see InsertSuffix and traverseSuffix) -- so prefixTrie is +// what the file is named after. +// +// prefixTrie holds the string-prefix constraints recorded for one level of the +// rule index: what `startswith(input.x, "/api/")` and +// `strings.any_prefix_match(input.x, [...])` contribute. +// +// A scalar constraint is answered with a map lookup, but a prefix constraint +// has to answer "which of the recorded prefixes does this value start with", +// and the answer is a set, not a single entry. Testing the value against every +// recorded prefix in turn costs O(p) string comparisons per lookup for p +// prefixes -- which is the work strings.any_prefix_match exists to avoid doing +// in the rule body, so doing it in the index instead would be no bargain. +// +// This is a compressed (radix) trie instead: a lookup walks the value once and +// costs O(len(value)) byte comparisons whatever p is. Compressed rather than +// one node per byte because the node count is then bounded by 2p-1 rather than +// by the total length of all prefixes -- 10k prefixes cost thousands of nodes, +// not hundreds of thousands. +type prefixTrie struct { + // edges are sorted by the first byte of their label, which is unique among + // them, so a step down the trie is a binary search. + edges []prefixEdge + // child is where the rules of the prefixes ending exactly here hang off. It + // is an ordinary trieNode, so whatever a rule constrains below a prefix + // constraint indexes as usual. + child *trieNode +} + +type prefixEdge struct { + label string + // node is the trie under this edge; leaf stands in for it when nothing is + // recorded past the edge's label, which is almost every edge. + node *prefixTrie + leaf *trieNode +} + +// edge locates the edge labelled with first byte b, or the position a new one +// would be inserted at to keep edges sorted. Only that byte is matched; labels +// are compressed, so comparing the rest of one is left to the caller. +func (p *prefixTrie) edge(b byte) (int, bool) { + return slices.BinarySearchFunc(p.edges, b, func(e prefixEdge, b byte) int { + return int(e.label[0]) - int(b) + }) +} + +// insert returns the node that the rules constrained by prefix hang off, +// creating it if this is the first time the prefix is recorded. +func (p *prefixTrie) insert(prefix string) *trieNode { + node := p + + for { + if prefix == "" { + if node.child == nil { + node.child = newTrieNodeImpl() + } + return node.child + } + + pos, found := node.edge(prefix[0]) + if !found { + leaf := newTrieNodeImpl() + node.edges = slices.Insert(node.edges, pos, prefixEdge{label: prefix, leaf: leaf}) + return leaf + } + + edge := node.edges[pos] + common := commonPrefixLen(edge.label, prefix) + + switch { + // The two diverge inside this edge -- "/api/v1" meeting "/api/v2" -- + // so the edge is split where they stop agreeing and what used to hang + // off it moves down onto the tail, whichever kind it is. + case common < len(edge.label): + tail := prefixEdge{label: edge.label[common:], node: edge.node, leaf: edge.leaf} + node.edges[pos] = prefixEdge{ + label: edge.label[:common], + node: &prefixTrie{edges: []prefixEdge{tail}}, + } + + // The prefix ends where an edge does with nothing past it, so its + // continuation is already the answer. + case common == len(prefix) && edge.leaf != nil: + return edge.leaf + + // Something is recorded past the edge now, so its continuation becomes + // the child of a trie of its own. + case edge.node == nil: + node.edges[pos] = prefixEdge{ + label: edge.label, + node: &prefixTrie{child: edge.leaf}, + } + } + + node = node.edges[pos].node + prefix = prefix[common:] + } +} + +// traverse visits the continuation of every recorded prefix that s starts with. +// One walk down the trie finds all of them: the prefixes of s that are in the +// trie are exactly the ends-of-prefix passed on the way down. +func (p *prefixTrie) traverse(s string, resolver ValueResolver, tr *trieTraversalResult) error { + for node := p; node != nil; { + if node.child != nil { + if err := node.child.Traverse(resolver, tr); err != nil { + return err + } + } + + if s == "" { + return nil + } + + pos, found := node.edge(s[0]) + if !found { + return nil + } + + edge := node.edges[pos] + if len(edge.label) > len(s) || s[:len(edge.label)] != edge.label { + return nil + } + + s = s[len(edge.label):] + if edge.node == nil { + return edge.leaf.Traverse(resolver, tr) + } + node = edge.node + } + + return nil +} + +// traverseSuffix is traverse over the end of s. A suffix trie holds its base +// strings reversed (see affixTries), so the walk consumes s from its +// last byte back -- which needs no reversed copy of s to be made per lookup. +func (p *prefixTrie) traverseSuffix(s string, resolver ValueResolver, tr *trieTraversalResult) error { + for node := p; node != nil; { + if node.child != nil { + if err := node.child.Traverse(resolver, tr); err != nil { + return err + } + } + + if s == "" { + return nil + } + + pos, found := node.edge(s[len(s)-1]) + if !found { + return nil + } + + edge := node.edges[pos] + if len(edge.label) > len(s) || !equalReversed(s[len(s)-len(edge.label):], edge.label) { + return nil + } + + s = s[:len(s)-len(edge.label)] + if edge.node == nil { + return edge.leaf.Traverse(resolver, tr) + } + node = edge.node + } + + return nil +} + +// equalReversed reports whether tail read backwards is reversed. +func equalReversed(tail, reversed string) bool { + for i := range reversed { + if reversed[i] != tail[len(tail)-1-i] { + return false + } + } + return true +} + +// reverseString returns s with its bytes reversed. A base string is reversed +// once, when it is recorded; `endswith` is a byte comparison, so reversing +// bytes rather than runes is what makes a suffix of s a prefix of reversed s. +func reverseString(s string) string { + b := []byte(s) + slices.Reverse(b) + + // b was made here and is not written to again, so it can be handed over + // rather than copied a second time. + return util.ByteSliceToString(b) +} + +// compact releases the spare capacity in the edge slices. Edges arrive in +// arbitrary order, so they are placed by insertion and grow the way append does +// -- which leaves 60% of the slots unused across a large prefix set -- and +// nothing inserts once the index is built. slices.Clip only caps the capacity; +// releasing the block means copying out of it. +func (p *prefixTrie) compact() { + if p == nil { + return + } + + if cap(p.edges) > len(p.edges) { + exact := make([]prefixEdge, len(p.edges)) + copy(exact, p.edges) + p.edges = exact + } + + p.child.compact() + + for _, edge := range p.edges { + edge.node.compact() + edge.leaf.compact() + } +} + +func (p *prefixTrie) do(walker trieWalker) { + if p == nil { + return + } + + p.child.Do(walker) + + for _, edge := range p.edges { + edge.node.do(walker) + edge.leaf.Do(walker) + } +} + +func (p *prefixTrie) traverseUnknown(resolver ValueResolver, tr *trieTraversalResult) error { + if p == nil { + return nil + } + + if err := p.child.Traverse(resolver, tr); err != nil { + return err + } + + for _, edge := range p.edges { + if err := edge.node.traverseUnknown(resolver, tr); err != nil { + return err + } + if err := edge.leaf.Traverse(resolver, tr); err != nil { + return err + } + } + + return nil +} + +// prefixEntry is a prefix the trie holds, spelled out, with the node its rules +// hang off. +type prefixEntry struct { + prefix string + node *trieNode +} + +// walk returns the prefixes the trie holds, in lexicographic order. Only the +// index's debug rendering and its tests have any use for reading back what the +// compression made of them. +func (p *prefixTrie) walk() []prefixEntry { + if p == nil { + return nil + } + + var ( + entries []prefixEntry + collect func(node *prefixTrie, prefix string) + ) + + collect = func(node *prefixTrie, prefix string) { + if node.child != nil { + entries = append(entries, prefixEntry{prefix: prefix, node: node.child}) + } + for _, edge := range node.edges { + if edge.node == nil { + entries = append(entries, prefixEntry{prefix: prefix + edge.label, node: edge.leaf}) + continue + } + collect(edge.node, prefix+edge.label) + } + } + + collect(p, "") + + return entries +} + +func commonPrefixLen(a, b string) int { + n := min(len(a), len(b)) + i := 0 + for i < n && a[i] == b[i] { + i++ + } + return i +} + +// InsertPrefix records that the rules below this node require the value at ref +// to be a string starting with prefix. +func (node *trieNode) InsertPrefix(ref Ref, prefix Value) *trieNode { + level := node.level() + level.ref = ref + + s, ok := prefix.(String) + if !ok { + panic("illegal prefix value") + } + + return level.affixTrie(affixPrefix).insert(string(s)) +} + +// InsertSuffix records that the rules below this node require the value at ref +// to be a string ending with suffix. +func (node *trieNode) InsertSuffix(ref Ref, suffix Value) *trieNode { + level := node.level() + level.ref = ref + + s, ok := suffix.(String) + if !ok { + panic("illegal suffix value") + } + + return level.affixTrie(affixSuffix).insert(reverseString(string(s))) +} + +// traversePrefixes visits the rules whose prefix constraints value satisfies. +// +// strings.any_prefix_match takes a collection of search strings as readily as a +// single one, and holds if any of them starts with any of the base strings, so +// a collection is tested element by element -- the same way a scalar constraint +// is matched against the members of a collection (see +// traverseCollectionMembership). +func (d *levelDetail) traversePrefixes(resolver ValueResolver, tr *trieTraversalResult, value Value) error { + prefixes := d.prefixes + if prefixes == nil { + return nil + } + + if s, ok := value.(String); ok { + return prefixes.traverse(string(s), resolver, tr) + } + + checkMember := func(t *Term) error { + if s, ok := t.Value.(String); ok { + return prefixes.traverse(string(s), resolver, tr) + } + return nil + } + + switch col := value.(type) { + case *Array: + return col.Iter(checkMember) + case Set: + return col.Iter(checkMember) + case Object: + if o, ok := col.(*object); ok { + return o.Iter(func(_, v *Term) error { + return checkMember(v) + }) + } + return col.Iter(func(_, v *Term) error { + return checkMember(v) + }) + } + + return nil +} + +// traverseSuffixes visits the rules whose suffix constraints value satisfies. +// A collection is tested element by element, as for prefixes. +func (d *levelDetail) traverseSuffixes(resolver ValueResolver, tr *trieTraversalResult, value Value) error { + suffixes := d.suffixes + if suffixes == nil { + return nil + } + + if s, ok := value.(String); ok { + return suffixes.traverseSuffix(string(s), resolver, tr) + } + + checkMember := func(t *Term) error { + if s, ok := t.Value.(String); ok { + return suffixes.traverseSuffix(string(s), resolver, tr) + } + return nil + } + + switch col := value.(type) { + case *Array: + return col.Iter(checkMember) + case Set: + return col.Iter(checkMember) + case Object: + if o, ok := col.(*object); ok { + // doesn't allocate / escape + for _, node := range o.sortedKeys() { + if err := checkMember(node.value); err != nil { + return err + } + } + return nil + } + // allocates / escapes + return col.Iter(func(_, v *Term) error { + return checkMember(v) + }) + } + + return nil +} + +// updateStartsWith indexes `startswith(x, "base")`: x has to be a string +// starting with base for the rule to hold. +func (i *refindices) updateAffix(rule *Rule, expr *Expr, constants map[Var]Value, a affix) { + ref := i.resolveAndValidateRef(rule, rule.Head.Args, expr.Operand(0)) + if ref == nil { + return + } + + base, ok := constantString(expr.Operand(1), constants) + if !ok { + return + } + + i.insert(rule, &refindex{ref: i.table.intern(ref), Value: base, Affix: a}) +} + +// updateAnyPrefixMatch indexes `strings.any_prefix_match(x, base)`, which is +// a disjunction of startswith calls: each base string is recorded as an +// alternative prefix for x, the same way each element of `x in [...]` is +// recorded as an alternative value (see updateMemberRefInValue). +// +// The search operand has to be a single ref: a collection of search strings +// would need every element of one collection tested against the other, which +// is not a constraint on the value at any one ref. +func (i *refindices) updateAnyAffixMatch(rule *Rule, expr *Expr, constants map[Var]Value, a affix) { + ref := i.resolveAndValidateRef(rule, rule.Head.Args, expr.Operand(0)) + if ref == nil { + return + } + + base := expr.Operand(1).Value + if v, ok := base.(Var); ok { + resolved, ok := constants[v] + if !ok { + return + } + base = resolved + } + + if s, ok := base.(String); ok { + i.insert(rule, &refindex{ref: i.table.intern(ref), Value: s, Affix: a}) + return + } + + // Every base string has to be recorded, or the index would exclude rules + // the dropped ones would have matched -- so a base that isn't a collection + // of ground strings throughout leaves the rule unindexed rather than + // partly indexed. + bases, ok := groundStrings(base) + if !ok || len(bases) == 0 { + return + } + + i.insertAffixes(rule, ref, bases, a) +} + +// insertAffixes records a whole base collection at once, for either end of the +// value. insert() rescans the rule's indices on every call, which is quadratic +// over the thousands strings.any_prefix_match carries, so the scan happens once +// here instead. insertMembers is the same for `in`; the two dedup on different +// key types. +func (i *refindices) insertAffixes(rule *Rule, ref Ref, bases []Value, a affix) { + id := i.table.intern(ref) + + // concrete counts the values this rule already reaches ref by that survive + // insertPath's var-stripping, so that the alternatives the base adds can be + // weighed against them without a second scan (see refindices.alternate). + concrete := 0 + known := false + seen := make(map[String]struct{}, len(bases)) + for _, other := range i.rules[rule] { + if other.ref != id { + continue + } + known = true + if !other.isVar() { + concrete++ + } + if other.Affix == a { + if s, ok := other.Value.(String); ok { + seen[s] = struct{}{} + } + } + } + n := len(bases) + if known && n > 0 { + n-- + } + i.countN(id, n) + + // One refindex per base, laid down in a single block rather than allocated + // one at a time: a base collection runs to thousands of them. Duplicates + // leave slack at the end of the block, which the reslice below drops. + pos := len(i.rules[rule]) + indices := util.GrowPtrSlice(i.rules[rule], len(bases)) + + for _, base := range bases { + // groundStrings has established that every base is a String, and hands + // the Term's own Value over so that refindex.Value costs no second box. + key := base.(String) + if _, ok := seen[key]; ok { + continue + } + seen[key] = struct{}{} + concrete++ + + *indices[pos] = refindex{ref: id, Value: base, Affix: a} + pos++ + } + i.rules[rule] = indices[:pos] + + if concrete > 1 { + i.alternate(id, alternationTerminal) + } +} + +// constantString resolves term to a string literal, following one level of +// var binding recorded earlier in the rule body. +func constantString(term *Term, constants map[Var]Value) (String, bool) { + v := term.Value + if vr, ok := v.(Var); ok { + resolved, ok := constants[vr] + if !ok { + return "", false + } + v = resolved + } + + s, ok := v.(String) + return s, ok +} + +// groundStrings returns the members of an array or set literal, and reports +// false unless every one of them is a string. The member's own Value is what +// comes back, not the String inside it: every caller puts it straight into a +// refindex, and a Term is already holding it boxed. +func groundStrings(v Value) ([]Value, bool) { + var ( + until func(func(*Term) bool) bool + n int + ) + + switch col := v.(type) { + case *Array: + until, n = col.Until, col.Len() + case Set: + until, n = col.Until, col.Len() + default: + return nil, false + } + + // The base of a strings.any_prefix_match runs to thousands of strings, so + // the length is worth taking off the collection rather than growing into. + out := make([]Value, 0, n) + + // Until stops on the first member that is not a string, and reports having + // stopped -- which is the whole of "unless every one of them is a string". + if until(func(t *Term) bool { + _, ok := t.Value.(String) + if ok { + out = append(out, t.Value) + } + return !ok + }) { + return nil, false + } + + return out, true +} diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/index_debug.go b/vendor/github.com/open-policy-agent/opa/v1/ast/index_debug.go index 17d74072ea..18ca875b88 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/index_debug.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/index_debug.go @@ -12,23 +12,24 @@ import ( "github.com/open-policy-agent/opa/v1/util" ) -func (node *trieNode) mermaid() string { +// mermaid renders the trie, naming the rules by their bodies -- which the index +// holds, and the nodes only the ids of. +func (i *baseDocEqIndex) mermaid() string { var sb strings.Builder sb.WriteString("graph TD\n") nodeCounter := 0 nodeIDs := make(map[*trieNode]string) - node.mermaidFormat(&sb, &nodeCounter, nodeIDs, "") + i.root.mermaidFormat(&sb, &nodeCounter, nodeIDs, "", i.rules) return sb.String() } -func (node *trieNode) mermaidFormat(sb *strings.Builder, counter *int, nodeIDs map[*trieNode]string, parentID string) { +func (node *trieNode) mermaidFormat(sb *strings.Builder, counter *int, nodeIDs map[*trieNode]string, parentID string, rules []*Rule) { currentID, exists := nodeIDs[node] if !exists { currentID = fmt.Sprintf("n%d", *counter) *counter++ nodeIDs[node] = currentID - label := node.mermaidLabel() - fmt.Fprintf(sb, " %s[\"%s\"]\n", currentID, label) + fmt.Fprintf(sb, " %s[\"%s\"]\n", currentID, node.mermaidLabel(rules)) } if parentID != "" { @@ -39,101 +40,96 @@ func (node *trieNode) mermaidFormat(sb *strings.Builder, counter *int, nodeIDs m return } - if node.undefined != nil { - if childID, childExists := nodeIDs[node.undefined]; childExists { - fmt.Fprintf(sb, " %s -->|undefined| %s\n", currentID, childID) - } else { - node.undefined.mermaidFormat(sb, counter, nodeIDs, "") - fmt.Fprintf(sb, " %s -->|undefined| %s\n", currentID, nodeIDs[node.undefined]) - } + node.next.mermaidFormat(sb, counter, nodeIDs, currentID, rules) +} + +// mermaidEdge draws one way of matching the level's reference, emitting the +// child first if this is where it is reached from. +func mermaidEdge(sb *strings.Builder, counter *int, nodeIDs map[*trieNode]string, from, label string, child *trieNode, rules []*Rule) { + if child == nil { + return + } + if _, exists := nodeIDs[child]; !exists { + child.mermaidFormat(sb, counter, nodeIDs, "", rules) } + fmt.Fprintf(sb, " %s -->|\"%s\"| %s\n", from, mermaidEscape(label), nodeIDs[child]) +} - if node.any != nil { - if childID, childExists := nodeIDs[node.any]; childExists { - fmt.Fprintf(sb, " %s -->|any| %s\n", currentID, childID) - } else { - node.any.mermaidFormat(sb, counter, nodeIDs, "") - fmt.Fprintf(sb, " %s -->|any| %s\n", currentID, nodeIDs[node.any]) - } +func (d *levelDetail) mermaidFormat(sb *strings.Builder, counter *int, nodeIDs map[*trieNode]string, from string, rules []*Rule) { + if d == nil { + return } - if node.scalars.Len() > 0 { - type scalarPair struct { - key Value - node *trieNode - } - pairs := make([]scalarPair, 0, node.scalars.Len()) - node.scalars.Iter(func(key Value, val *trieNode) bool { - pairs = append(pairs, scalarPair{key, val}) + mermaidEdge(sb, counter, nodeIDs, from, "undefined", d.undefined, rules) + mermaidEdge(sb, counter, nodeIDs, from, "any", d.any, rules) + + d.scalars.Iter(func(key Value, child *trieNode) bool { + mermaidEdge(sb, counter, nodeIDs, from, key.String(), child, rules) + return false + }) + + if d.alternatives != nil { + d.alternatives.members.Iter(func(key Value, nodes []*trieNode) bool { + for _, child := range nodes { + mermaidEdge(sb, counter, nodeIDs, from, key.String(), child, rules) + } return false }) - slices.SortFunc(pairs, func(a, b scalarPair) int { - return a.key.Compare(b.key) - }) - for _, pair := range pairs { - var scalarLabel string - if s, ok := pair.key.(String); ok { - scalarLabel = string(s) - } else { - scalarLabel = pair.key.String() - } - if len(scalarLabel) > 20 { - scalarLabel = scalarLabel[:20] + "..." - } - scalarLabel = mermaidEscape(scalarLabel) - if childID, childExists := nodeIDs[pair.node]; childExists { - fmt.Fprintf(sb, " %s -->|\"%s\"| %s\n", currentID, scalarLabel, childID) - } else { - pair.node.mermaidFormat(sb, counter, nodeIDs, "") - fmt.Fprintf(sb, " %s -->|\"%s\"| %s\n", currentID, scalarLabel, nodeIDs[pair.node]) - } - } } - if node.array != nil { - if childID, childExists := nodeIDs[node.array]; childExists { - fmt.Fprintf(sb, " %s -->|array| %s\n", currentID, childID) - } else { - node.array.mermaidFormat(sb, counter, nodeIDs, "") - fmt.Fprintf(sb, " %s -->|array| %s\n", currentID, nodeIDs[node.array]) - } + for _, p := range d.prefixes.walk() { + mermaidEdge(sb, counter, nodeIDs, from, p.prefix+"*", p.node, rules) } - if node.next != nil { - node.next.mermaidFormat(sb, counter, nodeIDs, currentID) + // A suffix trie holds its bases reversed (see affixTries), so what it + // walks back is what was written. + for _, p := range d.suffixes.walk() { + mermaidEdge(sb, counter, nodeIDs, from, "*"+reverseString(p.prefix), p.node, rules) } + + d.array.mermaidFormat(sb, counter, nodeIDs, from, "array", rules) } -func (node *trieNode) mermaidLabel() string { +func (a *arrayTrie) mermaidFormat(sb *strings.Builder, counter *int, nodeIDs map[*trieNode]string, from, label string, rules []*Rule) { + if a == nil { + return + } + + mermaidEdge(sb, counter, nodeIDs, from, label, a.end, rules) + a.any.mermaidFormat(sb, counter, nodeIDs, from, label+" any", rules) + a.scalars.Iter(func(key Value, child *arrayTrie) bool { + child.mermaidFormat(sb, counter, nodeIDs, from, label+" "+key.String(), rules) + return false + }) +} + +func (node *trieNode) mermaidLabel(rules []*Rule) string { var parts []string - if len(node.ref) > 0 { - parts = append(parts, node.ref.String()) + if node.next != nil && len(node.next.ref) > 0 { + parts = append(parts, node.next.ref.String()) } - if len(node.rules) > 0 { - for _, rn := range node.rules { - bodyStr := "" - if rn.rule.Body != nil { - bodyStr = rn.rule.Body.String() - if len(bodyStr) > 50 { - bodyStr = bodyStr[:50] + "..." - } + for _, id := range node.rules { + bodyStr := "" + if rule := rules[id]; rule.Body != nil { + bodyStr = rule.Body.String() + if len(bodyStr) > 50 { + bodyStr = bodyStr[:50] + "..." } - bodyStr = mermaidEscape(bodyStr) - parts = append(parts, bodyStr) } + parts = append(parts, mermaidEscape(bodyStr)) } - if len(node.mappers) > 0 { - parts = append(parts, fmt.Sprintf("%d mapper(s)", len(node.mappers))) + if node.next != nil && len(node.next.mappers) > 0 { + parts = append(parts, fmt.Sprintf("%d mapper(s)", len(node.next.mappers))) } if node.multiple { parts = append(parts, "multiple") } if len(parts) == 0 { - return "·" + return "\u00b7" } return strings.Join(parts, "
") @@ -151,25 +147,22 @@ func (node *trieNode) String() string { } func (node *trieNode) format(sb *strings.Builder, depth int) { + if node == nil { + return + } + indent := strings.Repeat(" ", depth) - if len(node.ref) > 0 { - sb.WriteString(indent) - sb.WriteString(node.ref.String()) - } else if depth == 0 { + if depth == 0 { sb.WriteString("root") + } else { + sb.WriteString(indent) } - if len(node.rules) > 0 { sb.WriteString(" [") util.WriteInt(sb, len(node.rules)) sb.WriteString(" rule(s)]") } - if len(node.mappers) > 0 { - sb.WriteString(" [") - util.WriteInt(sb, len(node.mappers)) - sb.WriteString(" mapper(s)]") - } if node.value != nil { sb.WriteString(" value=") sb.WriteString(node.value.String()) @@ -179,48 +172,134 @@ func (node *trieNode) format(sb *strings.Builder, depth int) { } sb.WriteByte('\n') - if node.undefined != nil { + node.next.format(sb, depth) +} + +// format prints the level below a node: the reference it resolves, and a line +// per way of matching it. +func (d *levelDetail) format(sb *strings.Builder, depth int) { + if d == nil { + return + } + + indent := strings.Repeat(" ", depth) + + if len(d.ref) > 0 { + sb.WriteString(indent) + sb.WriteString(d.ref.String()) + if len(d.mappers) > 0 { + sb.WriteString(" [") + util.WriteInt(sb, len(d.mappers)) + sb.WriteString(" mapper(s)]") + } + sb.WriteByte('\n') + } + + if d.undefined != nil { sb.WriteString(indent) sb.WriteString(" undefined:\n") - node.undefined.format(sb, depth+2) + d.undefined.format(sb, depth+2) } - if node.any != nil { + if d.any != nil { sb.WriteString(indent) sb.WriteString(" any:\n") - node.any.format(sb, depth+2) + d.any.format(sb, depth+2) } - if node.scalars.Len() > 0 { - scalars := make([]Value, 0, node.scalars.Len()) - nodes := make([]*trieNode, 0, node.scalars.Len()) - node.scalars.Iter(func(key Value, val *trieNode) bool { + if d.scalars.Len() > 0 { + scalars := make([]Value, 0, d.scalars.Len()) + d.scalars.Iter(func(key Value, _ *trieNode) bool { scalars = append(scalars, key) - nodes = append(nodes, val) return false }) slices.SortFunc(scalars, Value.Compare) - for i := range scalars { + for _, k := range scalars { + child, _ := d.scalars.Get(k) sb.WriteString(indent) sb.WriteString(" ") - sb.WriteString(scalars[i].String()) + sb.WriteString(k.String()) sb.WriteString(":\n") - for j := range nodes { - if ValueEqual(scalars[i], scalars[j]) { - nodes[j].format(sb, depth+2) - break + child.format(sb, depth+2) + } + } + + // Several values reaching one node, so the node is printed once and the + // values that reach it are named together (see alternativeChildren). + if d.alternatives != nil { + for i, conv := range d.alternatives.converged { + var keys []Value + d.alternatives.members.Iter(func(k Value, nodes []*trieNode) bool { + if slices.Contains(nodes, conv) { + keys = append(keys, k) } + return false + }) + slices.SortFunc(keys, Value.Compare) + + sb.WriteString(indent) + sb.WriteString(" any of ") + for j, k := range keys { + if j > 0 { + sb.WriteString(", ") + } + sb.WriteString(k.String()) } + fmt.Fprintf(sb, " -> #%d:\n", i) + conv.format(sb, depth+2) } } - if node.array != nil { + if d.array != nil { sb.WriteString(indent) sb.WriteString(" array:\n") - node.array.format(sb, depth+2) + d.array.format(sb, depth+2) + } + + for _, p := range d.prefixes.walk() { + sb.WriteString(indent) + sb.WriteString(` prefix "`) + sb.WriteString(p.prefix) + sb.WriteString("\":\n") + p.node.format(sb, depth+2) + } + + for _, p := range d.suffixes.walk() { + sb.WriteString(indent) + sb.WriteString(` suffix "`) + sb.WriteString(reverseString(p.prefix)) + sb.WriteString("\":\n") + p.node.format(sb, depth+2) + } +} + +func (a *arrayTrie) format(sb *strings.Builder, depth int) { + if a == nil { + return } - if node.next != nil { - node.next.format(sb, depth) + indent := strings.Repeat(" ", depth) + + a.end.format(sb, depth) + + if a.any != nil { + sb.WriteString(indent) + sb.WriteString(" any:\n") + a.any.format(sb, depth+2) + } + + keys := make([]Value, 0, a.scalars.Len()) + a.scalars.Iter(func(k Value, _ *arrayTrie) bool { + keys = append(keys, k) + return false + }) + slices.SortFunc(keys, Value.Compare) + for _, k := range keys { + child, _ := a.scalars.Get(k) + sb.WriteString(indent) + sb.WriteString(" ") + sb.WriteString(k.String()) + sb.WriteString(":\n") + child.format(sb, depth+2) } } diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/internal/scanner/scanner.go b/vendor/github.com/open-policy-agent/opa/v1/ast/internal/scanner/scanner.go index 6b2b03b27a..cbf4cd4799 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/internal/scanner/scanner.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/internal/scanner/scanner.go @@ -502,12 +502,9 @@ func (s *Scanner) scanRawTemplateString() (string, tokens.Token) { break } - if ch == '\\' { - switch s.curr { - case '{': - escapes = append(escapes, s.offset-1) - s.next() - } + if ch == '\\' && s.curr == '{' { + escapes = append(escapes, s.offset-1) + s.next() } } diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/interning.go b/vendor/github.com/open-policy-agent/opa/v1/ast/interning.go index 7e12367aba..7b604d5661 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/interning.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/interning.go @@ -18,6 +18,9 @@ type interned struct { } type internedRefs struct { + AnyPrefixMatch Ref + AnySuffixMatch Ref + EndsWith Ref Equal Ref Equality Ref GlobMatch Ref @@ -29,6 +32,7 @@ type internedRefs struct { Print Ref RegoMetadataChain Ref RegoMetadataRule Ref + StartsWith Ref } // NOTE! Great care must be taken **not** to modify the terms returned @@ -39,6 +43,9 @@ type internedRefs struct { var ( Interned = &interned{ Refs: &internedRefs{ + AnyPrefixMatch: AnyPrefixMatch.Ref(), + AnySuffixMatch: AnySuffixMatch.Ref(), + EndsWith: EndsWith.Ref(), Equal: Equal.Ref(), Equality: Equality.Ref(), GlobMatch: GlobMatch.Ref(), @@ -50,6 +57,7 @@ var ( Print: Print.Ref(), RegoMetadataChain: RegoMetadataChain.Ref(), RegoMetadataRule: RegoMetadataRule.Ref(), + StartsWith: StartsWith.Ref(), }, } diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/location/location.go b/vendor/github.com/open-policy-agent/opa/v1/ast/location/location.go index 9c2213bc5b..795480c1b9 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/location/location.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/location/location.go @@ -129,7 +129,7 @@ func EndOf(row, col int, text []byte) (endRow, endCol int) { // column of the Location (but not on the text.) Nil locations are greater than // non-nil locations. func (loc *Location) Compare(other *Location) int { - if loc == other { + if loc == other { //nolint:gocritic // this is fine as an ifElseChain return 0 } else if loc == nil { return 1 diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/map.go b/vendor/github.com/open-policy-agent/opa/v1/ast/map.go index aa0e655b9e..2120e00b0d 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/map.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/map.go @@ -26,7 +26,7 @@ func NewValueMap() *ValueMap { // MarshalJSON provides a custom marshaller for the ValueMap which // will include the key, value, and value type. func (vs *ValueMap) MarshalJSON() ([]byte, error) { - var tmp []map[string]any + tmp := make([]map[string]any, 0, vs.Len()) vs.Iter(func(k Value, v Value) bool { tmp = append(tmp, map[string]any{ "name": k.String(), diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/parser.go b/vendor/github.com/open-policy-agent/opa/v1/ast/parser.go index b1d95af6ed..65a3a2a18a 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/parser.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/parser.go @@ -62,7 +62,7 @@ var ( // recursion exceeds the maximum allowed depth ErrMaxParsingRecursionDepthExceeded = errors.New("max parsing recursion depth exceeded") - RegoV1CompatibleRef = Ref{VarTerm("rego"), InternedTerm("v1")} + RegoV1CompatibleRef = Ref{RegoRootDocument, InternedTerm("v1")} // this is the name to use for instantiating an empty set, e.g., `set()`. setConstructor = RefTerm(VarTerm("set")) @@ -73,6 +73,7 @@ var ( } metadataBytes = []byte("METADATA") metadataParserPool = util.NewSyncPool[metadataParser]() + noScanOptions []scanner.ScanOption ) func (v RegoVersion) Int() int { @@ -443,6 +444,14 @@ func (p *Parser) Parse() ([]Statement, []*Comment, Errors) { // point trying to parse packages, imports, etc. in the same order. for p.s.tok != tokens.EOF { var s *state + + // Reported here rather than in parseRules: `package := 1` and `import := 1` + // are consumed by the statement parsers below, which fail pointing at the + // assign token instead of the keyword. + if !p.po.SkipRules && p.errKeywordRuleName(false) { + break + } + if p.s.tok == tokens.Package { s = p.save() if pkg := p.parsePackage(); pkg != nil { @@ -459,8 +468,10 @@ func (p *Parser) Parse() ([]Statement, []*Comment, Errors) { if imp := p.parseImport(); imp != nil { if RegoRootDocument.Equal(imp.Path.Value.(Ref)[0]) { p.regoV1Import(imp) + p.reclassifyKeyword() } else if FutureRootDocument.Equal(imp.Path.Value.(Ref)[0]) { p.futureImport(imp, allowedFutureKeywords) + p.reclassifyKeyword() } stmts = append(stmts, imp) continue @@ -766,6 +777,74 @@ func scanAheadRef(p *Parser) bool { return false } +// keywordRuleNameFollowers maps a keyword token to the tokens that, following it, +// make the statement unambiguously a rule declaration. +var ( + ruleNameFollowers = []tokens.Token{tokens.Assign, tokens.Unify, tokens.If, tokens.Contains, tokens.LParen} + // `not`/`and`/`or` drop '(': at the start of a statement, `not (x)` is a negated + // group and `or(x, y)` a call to the set union built-in, not rule heads. + operatorRuleNameFollowers = []tokens.Token{tokens.Assign, tokens.Unify, tokens.If, tokens.Contains} + // `package`/`import` drop `if` and `contains`: both take a path that may itself + // be named after a keyword, as in `package contains` or `import if.foo`. + pathRuleNameFollowers = []tokens.Token{tokens.Assign, tokens.Unify, tokens.LParen} + keywordRuleNameFollowers = map[tokens.Token][]tokens.Token{ + tokens.Every: ruleNameFollowers, + tokens.If: ruleNameFollowers, + tokens.In: ruleNameFollowers, + tokens.Some: ruleNameFollowers, + tokens.As: ruleNameFollowers, + tokens.Package: pathRuleNameFollowers, + tokens.Import: pathRuleNameFollowers, + tokens.Not: operatorRuleNameFollowers, + tokens.LogicalAnd: operatorRuleNameFollowers, + tokens.LogicalOr: operatorRuleNameFollowers, + // `contains` outside of a rule head parses as a plain var, so `contains := x` + // is still a legal query; as a rule it's caught by the rego-v1 check. + tokens.Contains: {tokens.If, tokens.Contains}, + } +) + +// reclassifyKeyword re-tags the lookahead token after an import that registered +// new keywords with the scanner. The parser reads one token ahead, so the first +// token of the statement following the import was scanned before the scanner +// knew about the keyword, and would otherwise be treated as a plain identifier. +func (p *Parser) reclassifyKeyword() { + if p.s.tok != tokens.Ident { + return + } + + if tok, ok := allFutureKeywords[p.s.lit]; ok && p.s.s.IsKeyword(p.s.lit) { + p.s.tok = tok + } +} + +// errKeywordRuleName reports an error if the current token is a keyword used as a +// rule name, e.g. `every := 1`, and returns whether it did. A statement that began +// with `default` can only be a rule, so no lookahead is needed there. +func (p *Parser) errKeywordRuleName(isDefault bool) bool { + followers, ok := keywordRuleNameFollowers[p.s.tok] + if !ok { + return false + } + + keyword, loc := p.s.tok, p.s.Loc() + + if !isDefault { + s := p.save() + p.scan() + next := p.s.tok + p.restore(s) + + if !slices.Contains(followers, next) { + return false + } + } + + p.errorf(loc, "%s keyword cannot be used for rule name", keyword) + + return true +} + // scanAheadLogicalCall rewrites an `and`/`or` keyword token to tokens.Ident when // it's immediately followed by `(`. Only valid where a term is expected: there, // the operator reading is impossible, so it must be a function (`&`/`|` set built-ins). @@ -801,6 +880,9 @@ func (p *Parser) parseRules() []*Rule { } if p.s.tok != tokens.Ident { + if rule.Default { + p.errKeywordRuleName(true) + } return nil } @@ -1191,6 +1273,7 @@ func (p *Parser) parseQuery(requireSemi bool, end tokens.Token) Body { if !p.s.skippedNL { // If there was already an error then don't pile this one on if len(p.s.errors) == 0 { + p.hintMissingInfixKeyword() p.illegal(`expected \n or %s or %s`, tokens.Semicolon, end) } return nil @@ -1222,6 +1305,7 @@ func (p *Parser) parseLiteral() (expr *Expr) { // binary. Otherwise, restore and fall through to regular handling. if p.s.tok == tokens.LBrace && p.logicalKeywordsActive() { s := p.save() + cache := p.cache.m braceOffset := p.s.loc.Offset bodyLoc := p.s.Loc() p.scan() @@ -1243,6 +1327,7 @@ func (p *Parser) parseLiteral() (expr *Expr) { } } p.restore(s) + p.cache.m = cache } // LHS/whole parenthesized group at statement start: `(a or b)`, @@ -1471,6 +1556,60 @@ func (p *Parser) attachWith(e *Expr) *Expr { return e } +// infixFutureKeywords are the future keywords usable as infix operators in a +// rule body, mapped to an example of the expression each enables. +var infixFutureKeywords = map[string]string{ + "in": "x in xs", + "and": "x and y", + "or": "x or y", +} + +// hintMissingInfixKeyword hints at the import for a body expression trailed by +// a plain `in`/`and`/`or` identifier, or by the comma of `k, v in xs`. Only +// call it when an error is about to be reported: an unconsumed hint would end +// up attached to a later, unrelated error. +func (p *Parser) hintMissingInfixKeyword() { + // Something more specific, like `some x in xs`, already hinted. + if len(p.s.hints) > 0 { + return + } + + kw := "in" + + switch p.s.tok { + case tokens.Ident: + // An active keyword scans as its own token, so an Ident means it's + // the import that's missing. + kw = p.s.lit + if _, ok := infixFutureKeywords[kw]; !ok { + return + } + case tokens.Comma: + // Only hint on `k, v in xs`, so require a membership expr after the comma. + s := p.save() + p.scan() + term := p.futureParser().parseTermInfixCall() + p.restore(s) + + if term == nil { + return + } + call, ok := term.Value.(Call) + if !ok || len(call) == 0 { + return + } + switch call[0].String() { + case Member.Name, MemberWithKey.Name: + default: + return + } + default: + return + } + + p.hint(fmt.Sprintf("`import future.keywords.%s` for `%s` expressions", kw, infixFutureKeywords[kw])) +} + func (p *Parser) errWithOnOperand(loc *Location, kw string) { p.hint(fmt.Sprintf( "Wrap the operand in `(...)` or `{...}` to scope, or move `with` after the `%s` expression to apply it to the whole expression", @@ -3076,12 +3215,14 @@ func (p *Parser) parseObject(k *Term, potentialComprehension bool) *Term { return nil } - potentialRelation := true if potentialComprehension { switch p.s.tok { case tokens.RBrace, tokens.Comma: - potentialRelation = false - fallthrough + // This is the only parse available, so return its result as-is: + // backtracking would drop the errors reported here in favour of a + // "non-terminated object" pointing at the value we just parsed + // rather than at the offending token. + return p.parseObjectFinish(k, v, true) case tokens.Or: if term := p.parseObjectFinish(k, v, true); term != nil { return term @@ -3091,16 +3232,14 @@ func (p *Parser) parseObject(k *Term, potentialComprehension bool) *Term { p.restore(s) - if potentialRelation { - v := p.parseTermInfixCallInList() - if v == nil { - return nil - } + v = p.parseTermInfixCallInList() + if v == nil { + return nil + } - switch p.s.tok { - case tokens.RBrace, tokens.Comma: - return p.parseObjectFinish(k, v, false) - } + switch p.s.tok { + case tokens.RBrace, tokens.Comma: + return p.parseObjectFinish(k, v, false) } p.illegal("non-terminated object") @@ -3322,8 +3461,6 @@ func (p *Parser) illegalToken() { p.illegal("") } -var noScanOptions []scanner.ScanOption - func (p *Parser) scan() { p.doScan(true, noScanOptions...) } diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/parser_ext.go b/vendor/github.com/open-policy-agent/opa/v1/ast/parser_ext.go index 4ee762514a..6c8537c222 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/parser_ext.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/parser_ext.go @@ -703,7 +703,12 @@ func parseModule(filename string, stmts []Statement, comments []*Comment, regoCo case Body: rule, err := ParseRuleFromBody(mod, stmt) if err != nil { - errs = append(errs, NewError(ParseErr, stmt[0].Location, "%s", err.Error())) + msg := err.Error() + if kw, ok := missingHeadKeyword(mod.regoVersion, stmt, stmts, i+1); ok { + msg = fmt.Sprintf("%s (hint: `import future.keywords.%s` for `%s` rules)", + msg, kw, headFutureKeywords[kw]) + } + errs = append(errs, NewError(ParseErr, stmt[0].Location, "%s", msg)) continue } rule.generatedBody = true @@ -747,6 +752,66 @@ func parseModule(filename string, stmts []Statement, comments []*Comment, regoCo return mod, nil } +// headFutureKeywords are the future keywords used in a rule head, mapped to an +// example of the rule form each enables. +var headFutureKeywords = map[string]string{ + "if": "p if { ... }", + "contains": "p contains x", +} + +// missingHeadKeyword reports the rule-head future keyword a statement was +// misparsed around. Unimported, the keyword is just a var, so `p if { ... }` +// parses as the body `p` followed by a rule named `if`. idx is stmt's index +// within stmts. +func missingHeadKeyword(v RegoVersion, stmt Body, stmts []Statement, idx int) (string, bool) { + // From v1 on these are ordinary keywords. + if v != RegoV0 { + return "", false + } + + if kw, ok := statementHeadKeyword(stmt); ok { + return kw, true + } + + // The keyword starts its own statement; check the next one on the same line. + if idx+1 >= len(stmts) { + return "", false + } + next := stmts[idx+1] + if next.Loc() == nil || stmt.Loc() == nil || next.Loc().Row != stmt.Loc().Row { + return "", false + } + + return statementHeadKeyword(next) +} + +// statementHeadKeyword returns the rule-head future keyword a statement was +// reduced to: a rule named after it, or a body holding only it as a var. +func statementHeadKeyword(stmt Statement) (string, bool) { + var name Var + + switch stmt := stmt.(type) { + case *Rule: + name = stmt.Head.Name + case Body: + if len(stmt) != 1 { + return "", false + } + term, ok := stmt[0].Terms.(*Term) + if !ok { + return "", false + } + if name, ok = term.Value.(Var); !ok { + return "", false + } + default: + return "", false + } + + _, ok := headFutureKeywords[string(name)] + return string(name), ok +} + func ruleDeclarationHasKeyword(rule *Rule, keyword tokens.Token) bool { return slices.Contains(rule.Head.keywords, keyword) } diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/policy.go b/vendor/github.com/open-policy-agent/opa/v1/ast/policy.go index 863c9ad599..b2abc34659 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/policy.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/policy.go @@ -36,6 +36,8 @@ var FunctionArgRootDocument = VarTerm("args") // features. var FutureRootDocument = VarTerm("future") +var FutureKeywordsRef = Ref{FutureRootDocument, InternedTerm("keywords")} + // RegoRootDocument names the document containing new, to-become-default, // features in a future versioned release. var RegoRootDocument = VarTerm("rego") @@ -225,7 +227,6 @@ type ( // Rule represents a rule as defined in the language. Rules define the // content of documents that represent policy decisions. Rule struct { - Default bool `json:"default,omitempty"` Head *Head `json:"head"` Body Body `json:"body"` Else *Rule `json:"else,omitempty"` @@ -238,6 +239,7 @@ type ( // on the rule (e.g., printing, comparison, visiting, etc.) Module *Module `json:"-"` + Default bool `json:"default,omitempty"` generatedBody bool } @@ -345,7 +347,7 @@ func (mod *Module) Compare(other *Module) int { if cmp := slices.CompareFunc(mod.Annotations, other.Annotations, (*Annotations).Compare); cmp != 0 { return cmp } - return rulesCompare(mod.Rules, other.Rules) + return slices.CompareFunc(mod.Rules, other.Rules, (*Rule).Compare) } // Copy returns a deep copy of mod. @@ -451,8 +453,7 @@ func (c *Comment) String() string { // Copy returns a deep copy of c. func (c *Comment) Copy() *Comment { cpy := *c - cpy.Text = make([]byte, len(c.Text)) - copy(cpy.Text, c.Text) + cpy.Text = slices.Clone(c.Text) return &cpy } @@ -466,7 +467,7 @@ func (c *Comment) Equal(other *Comment) bool { // Compare returns an integer indicating whether pkg is less than, equal to, // or greater than other. func (pkg *Package) Compare(other *Package) int { - return termSliceCompare(pkg.Path, other.Path) + return slices.CompareFunc(pkg.Path, other.Path, TermValueCompare) } // Copy returns a deep copy of pkg. @@ -511,10 +512,8 @@ func IsValidImportPath(v Value) (err error) { if err := IsValidImportPath(v[0].Value); err != nil { return fmt.Errorf("invalid path %v: path must begin with input or data", v) } - for _, e := range v[1:] { - if _, ok := e.Value.(String); !ok { - return fmt.Errorf("invalid path %v: path elements must be strings", v) - } + if !util.Every(v[1:], TermValueIs[String]) { + return fmt.Errorf("invalid path %v: path elements must be strings", v) } default: return fmt.Errorf("invalid path %v: path must be ref or var", v) @@ -823,19 +822,19 @@ func (head *Head) Compare(other *Head) int { } else if !head.Assign && other.Assign { return 1 } - if cmp := termSliceCompare(head.Args, other.Args); cmp != 0 { + if cmp := slices.CompareFunc(head.Args, other.Args, TermValueCompare); cmp != 0 { return cmp } - if cmp := termSliceCompare(head.Reference, other.Reference); cmp != 0 { + if cmp := slices.CompareFunc(head.Reference, other.Reference, TermValueCompare); cmp != 0 { return cmp } if cmp := VarCompare(head.Name, other.Name); cmp != 0 { return cmp } - if cmp := Compare(head.Key, other.Key); cmp != 0 { + if cmp := TermValueCompare(head.Key, other.Key); cmp != 0 { return cmp } - return Compare(head.Value, other.Value) + return TermValueCompare(head.Value, other.Value) } // Copy returns a deep copy of head. @@ -1077,7 +1076,7 @@ func (expr *Expr) Equal(other *Expr) bool { // // Otherwise, the expression terms are compared normally. If both expressions // have the same terms, the modifiers are compared. -func (expr *Expr) Compare(other *Expr) int { +func (expr *Expr) Compare(other *Expr) (c int) { switch { case expr == other: return 0 @@ -1111,36 +1110,25 @@ func (expr *Expr) Compare(other *Expr) int { switch t := expr.Terms.(type) { case *Term: - if cmp := t.Value.Compare(other.Terms.(*Term).Value); cmp != 0 { - return cmp - } + c = TermValueCompare(t, other.Terms.(*Term)) case []*Term: - if cmp := termSliceCompare(t, other.Terms.([]*Term)); cmp != 0 { - return cmp - } + c = slices.CompareFunc(t, other.Terms.([]*Term), TermValueCompare) case *SomeDecl: - if cmp := Compare(t, other.Terms.(*SomeDecl)); cmp != 0 { - return cmp - } + c = t.Compare(other.Terms.(*SomeDecl)) case *Every: - if cmp := Compare(t, other.Terms.(*Every)); cmp != 0 { - return cmp - } + c = t.Compare(other.Terms.(*Every)) case *Not: - if cmp := t.Compare(other.Terms.(*Not)); cmp != 0 { - return cmp - } + c = t.Compare(other.Terms.(*Not)) case *LogicalAnd: - if cmp := Compare(t, other.Terms.(*LogicalAnd)); cmp != 0 { - return cmp - } + c = t.Compare(other.Terms.(*LogicalAnd)) case *LogicalOr: - if cmp := Compare(t, other.Terms.(*LogicalOr)); cmp != 0 { - return cmp - } + c = t.Compare(other.Terms.(*LogicalOr)) } - return withSliceCompare(expr.With, other.With) + if c == 0 { + c = slices.CompareFunc(expr.With, other.With, (*With).Compare) + } + return c } func (expr *Expr) sortOrder() int { @@ -1208,9 +1196,7 @@ func (expr *Expr) Hash() int { case *SomeDecl: s += ts.Hash() case []*Term: - for _, t := range ts { - s += t.Value.Hash() - } + s += termSliceHash(ts) case *Term: s += ts.Value.Hash() case *LogicalAnd: @@ -1343,10 +1329,8 @@ func (expr *Expr) Operands() []*Term { func (expr *Expr) IsGround() bool { switch ts := expr.Terms.(type) { case []*Term: - for _, t := range ts[1:] { - if !t.IsGround() { - return false - } + if !util.Every(ts[1:], (*Term).IsGround) { + return false } case *Term: return ts.IsGround() @@ -1356,6 +1340,9 @@ func (expr *Expr) IsGround() bool { return ts.Lhs.IsGround() && ts.Rhs.IsGround() case *LogicalOr: return ts.Lhs.IsGround() && ts.Rhs.IsGround() + case *SomeDecl, *Every: + // Both declare local variables, which are never ground. + return false } return true } @@ -1471,7 +1458,7 @@ func (d *SomeDecl) Copy() *SomeDecl { // Compare returns an integer indicating whether d is less than, equal to, or // greater than other. func (d *SomeDecl) Compare(other *SomeDecl) int { - return termSliceCompare(d.Symbols, other.Symbols) + return slices.CompareFunc(d.Symbols, other.Symbols, TermValueCompare) } // Hash returns a hash code of d. @@ -1519,7 +1506,7 @@ func (q *Every) Compare(other *Every) int { {q.Value, other.Value}, {q.Domain, other.Domain}, } { - if d := Compare(terms[0], terms[1]); d != 0 { + if d := TermValueCompare(terms[0], terms[1]); d != 0 { return d } } @@ -1699,18 +1686,19 @@ func (w *With) Equal(other *With) bool { // Compare returns an integer indicating whether w is less than, equal to, or // greater than other. func (w *With) Compare(other *With) int { + if w == other { + return 0 + } if w == nil { - if other == nil { - return 0 - } return -1 - } else if other == nil { + } + if other == nil { return 1 } - if cmp := w.Target.Value.Compare(other.Target.Value); cmp != 0 { + if cmp := TermValueCompare(w.Target, other.Target); cmp != 0 { return cmp } - return w.Value.Value.Compare(other.Value.Value) + return TermValueCompare(w.Value, other.Value) } // Copy returns a deep copy of w. @@ -1780,6 +1768,12 @@ func Copy(x any) any { return x.Copy() case *ObjectComprehension: return x.Copy() + case *LogicalAnd: + return x.Copy() + case *LogicalOr: + return x.Copy() + case *TemplateString: + return x.Copy() case Set: return x.Copy() case *object: @@ -1820,12 +1814,7 @@ func (rs *RuleSet) Add(rule *Rule) { // Contains returns true if rs contains rule. func (rs RuleSet) Contains(rule *Rule) bool { - for i := range rs { - if rs[i].Equal(rule) { - return true - } - } - return false + return slices.ContainsFunc(rs, rule.Equal) } // Diff returns a new RuleSet containing rules in rs that are not in other. @@ -1846,10 +1835,7 @@ func (rs RuleSet) Equal(other RuleSet) bool { // Merge returns a ruleset containing the union of rules from rs an other. func (rs RuleSet) Merge(other RuleSet) RuleSet { - result := NewRuleSet() - for i := range rs { - result.Add(rs[i]) - } + result := NewRuleSet(rs...) for i := range other { result.Add(other[i]) } diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/policy_appenders.go b/vendor/github.com/open-policy-agent/opa/v1/ast/policy_appenders.go index ccc0554ad0..2373d8a89b 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/policy_appenders.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/policy_appenders.go @@ -21,7 +21,9 @@ func (mod *Module) AppendText(buf []byte) ([]byte, error) { // rule annotations are attached to rules, so only check for package scoped ones here if annotations.Scope == "package" || annotations.Scope == "subpackages" { buf = append(buf, "# METADATA\n# "...) - buf = append(buf, annotations.String()...) + if buf, err = annotations.AppendText(buf); err != nil { + return nil, err + } buf = append(buf, '\n') } } @@ -98,7 +100,10 @@ func (rule *Rule) appendWithOpts(opts toStringOpts, buf []byte) ([]byte, error) // See note in [Module.AppendText] regarding annotations. for _, annotations := range rule.Annotations { buf = append(buf, "# METADATA\n# "...) - buf = append(buf, annotations.String()...) + var err error + if buf, err = annotations.AppendText(buf); err != nil { + return nil, err + } buf = append(buf, '\n') } diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/rego_v1.go b/vendor/github.com/open-policy-agent/opa/v1/ast/rego_v1.go index db9e0f722c..64fe23bb50 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/rego_v1.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/rego_v1.go @@ -33,14 +33,15 @@ func checkRootDocumentOverrides(node any) Errors { } if ReservedVars.Contains(name) { - errors = append(errors, NewError(CompileErr, rule.Location, "rules must not shadow %v (use a different rule name)", name)) + errors = append(errors, + NewError(CompileErr, rule.Location, "rules must not shadow %v (use a different rule name)", name)) } for _, arg := range rule.Head.Args { - if _, ok := arg.Value.(Ref); ok { - if RootDocumentRefs.Contains(arg) { - errors = append(errors, NewError(CompileErr, arg.Location, "args must not shadow %v (use a different variable name)", arg)) - } + if _, ok := arg.Value.(Ref); ok && RootDocumentRefs.Contains(arg) { + errors = append(errors, NewError( + CompileErr, arg.Location, "args must not shadow %v (use a different variable name)", arg, + )) } } @@ -49,11 +50,12 @@ func checkRootDocumentOverrides(node any) Errors { WalkExprs(node, func(expr *Expr) bool { if expr.IsAssignment() { - // assign() can be called directly, so we need to assert its given first operand exists before checking its name. + // assign() can be called directly, so assert its given first operand exists before checking its name. if nameOp := expr.Operand(0); nameOp != nil { - name := Var(nameOp.String()) - if ReservedVars.Contains(name) { - errors = append(errors, NewError(CompileErr, expr.Location, "variables must not shadow %v (use a different variable name)", name)) + if name := Var(nameOp.String()); ReservedVars.Contains(name) { + errors = append(errors, NewError( + CompileErr, expr.Location, "variables must not shadow %v (use a different variable name)", name, + )) } } } diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/term.go b/vendor/github.com/open-policy-agent/opa/v1/ast/term.go index 6e4503d4d8..5a29c628e0 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/term.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/term.go @@ -244,7 +244,7 @@ func valueToInterface(v Value, resolver Resolver, opt JSONOpt) (any, error) { } return v.native, nil case Set: - buf := []any{} + buf := make([]any, 0, v.Len()) iter := func(x *Term) error { x1, err := valueToInterface(x.Value, resolver, opt) if err != nil { @@ -441,6 +441,12 @@ func TermValueIs[T Value](term *Term) (ok bool) { return ok } +// ToTerm exists solely to be able to map concrete Value +// implementations to *Term in util.Map, util.MapKeys, etc. +func ToTerm[T Value](v T) *Term { + return NewTerm(v) +} + // IsConstant returns true if the AST value is constant. // Note that this is only a shallow check as we currently don't have a real // notion of constant "vars" in the AST implementation. Meaning that while we could @@ -1197,8 +1203,7 @@ func (ref Ref) Concat(terms []*Term) Ref { // Dynamic returns the offset of the first non-constant operand of ref. func (ref Ref) Dynamic() int { - switch ref[0].Value.(type) { - case Call: + if TermValueIs[Call](ref[0]) { return 0 } for i := 1; i < len(ref); i++ { @@ -1235,27 +1240,15 @@ func (ref Ref) CopyNonGround() Ref { // Equal returns true if ref is equal to other. func (ref Ref) Equal(other Value) bool { - switch o := other.(type) { - case Ref: - if len(ref) == len(o) { - for i := range ref { - if !ref[i].Equal(o[i]) { - return false - } - } - - return true - } - } - - return false + o, ok := other.(Ref) + return ok && slices.EqualFunc(ref, o, (*Term).Equal) } // Compare compares ref to other, return <0, 0, or >0 if it is less than, equal to, // or greater than other. func (ref Ref) Compare(other Value) int { if o, ok := other.(Ref); ok { - return termSliceCompare(ref, o) + return slices.CompareFunc(ref, o, TermValueCompare) } return valueTypeCompare(ref, other) } @@ -1515,7 +1508,7 @@ func (arr *Array) Equal(other Value) bool { // or greater than other. func (arr *Array) Compare(other Value) int { if b, ok := other.(*Array); ok { - return termSliceCompare(arr.elems, b.elems) + return slices.CompareFunc(arr.elems, b.elems, TermValueCompare) } return valueTypeCompare(arr, other) @@ -1568,9 +1561,11 @@ func (arr *Array) Sorted() *Array { slices.SortFunc(cpy, TermValueCompare) - a := NewArray(cpy...) - a.hashs = arr.hashs - return a + // NewArray has already hashed cpy in its own order. Taking arr.hashs over + // that would leave hashs[i] holding the hash of some other element, which + // Array.set relies on, and would share the slice with arr so that setting + // an element here corrupted arr. + return NewArray(cpy...) } // Hash returns the hash code for the Value. @@ -1615,14 +1610,25 @@ func (arr *Array) rehash() { func (arr *Array) set(i int, v *Term) { arr.ground = arr.ground && v.IsGround() arr.elems[i] = v - arr.hashs[i] = v.Value.Hash() - arr.rehash() + + // arr.hash is the sum of arr.hashs, so swapping one element's hash in is + // enough -- rehashing the whole array here makes building an array of n + // elements O(n^2), which is felt on the large arrays that appear in + // generated policies. + h := v.Value.Hash() + arr.hash += h - arr.hashs[i] + arr.hashs[i] = h } // Slice returns a slice of arr starting from i index to j. -1 // indicates the end of the array. The returned value array is not a // copy and any modifications to either of arrays may be reflected to // the other. +// +// Set on the returned slice writes through to arr's element and to its +// hash, but not to arr's cached sum of those hashes, so arr.Hash() is +// stale from then on and anything holding arr as a map key or set member +// stops finding it. Copy the slice before writing to it. func (arr *Array) Slice(i, j int) *Array { var elems []*Term var hashs []int @@ -1667,10 +1673,11 @@ func (arr *Array) Foreach(f func(*Term)) { // Append appends a term to arr, returning the appended array. func (arr *Array) Append(v *Term) *Array { + vhs := v.Value.Hash() cpy := *arr cpy.elems = append(arr.elems, v) - cpy.hashs = append(arr.hashs, v.Value.Hash()) - cpy.hash = arr.hash + v.Value.Hash() + cpy.hashs = append(arr.hashs, vhs) + cpy.hash += vhs cpy.ground = arr.ground && v.IsGround() return &cpy } @@ -1897,13 +1904,9 @@ func (s *set) Foreach(f func(*Term)) { // Map returns a new Set obtained by applying f to each value in s. func (s *set) Map(f func(*Term) (*Term, error)) (Set, error) { - mapped := make([]*Term, 0, len(s.keys)) - for _, x := range s.sortedKeys() { - term, err := f(x) - if err != nil { - return nil, err - } - mapped = append(mapped, term) + mapped, err := util.TryMap(s.sortedKeys(), f) + if err != nil { + return nil, err } return NewSet(mapped...), nil } @@ -2193,12 +2196,7 @@ func (lob *lazyObj) Keys() []*Term { if lob.strict != nil { return lob.strict.Keys() } - ret := make([]*Term, 0, len(lob.native)) - for k := range lob.native { - ret = append(ret, InternedTerm(k)) - } - - return util.SortedFunc(ret, TermValueCompare) + return util.SortedFunc(util.MapKeys(lob.native, InternedTerm), TermValueCompare) } func (lob *lazyObj) KeysIterator() ObjectKeysIterator { @@ -2982,7 +2980,7 @@ func (c Call) Copy() Call { // or greater than other. func (c Call) Compare(other Value) int { if oc, ok := other.(Call); ok { - return termSliceCompare(c, oc) + return slices.CompareFunc(c, oc, TermValueCompare) } return valueTypeCompare(c, other) } @@ -3072,18 +3070,6 @@ func termSliceCopy(a []*Term) []*Term { return cpy } -func termSliceEqual(a, b []*Term) bool { - if len(a) == len(b) { - for i := range a { - if !a[i].Equal(b[i]) { - return false - } - } - return true - } - return false -} - func termSliceHash(a []*Term) int { var hash int for _, v := range a { diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/transform.go b/vendor/github.com/open-policy-agent/opa/v1/ast/transform.go index 84e9700018..36f414e549 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/transform.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/transform.go @@ -32,6 +32,11 @@ func Transform(t Transformer, x any) (any, error) { return nil, nil } + // The cases below that hold a slice mutate it in place, so this interface + // value goes on describing what they produce. Returning it costs nothing, + // where returning the case variable would box a slice header afresh. + orig := y + var ok bool switch y := y.(type) { case *Module: @@ -137,7 +142,7 @@ func Transform(t Transformer, x any) (any, error) { return nil, err } } - return y, nil + return orig, nil case Body: for i, e := range y { e, err := Transform(t, e) @@ -148,7 +153,7 @@ func Transform(t Transformer, x any) (any, error) { return nil, fmt.Errorf("illegal transform: %T != %T", y[i], e) } } - return y, nil + return orig, nil case *Expr: switch ts := y.Terms.(type) { case *SomeDecl: @@ -238,7 +243,7 @@ func Transform(t Transformer, x any) (any, error) { return nil, err } } - return y, nil + return orig, nil case *object: return y.Map(func(k, v *Term) (*Term, *Term, error) { k, err := transformTerm(t, k) @@ -260,14 +265,10 @@ func Transform(t Transformer, x any) (any, error) { y.set(i, v) } return y, nil - case Set: - y, err = y.Map(func(term *Term) (*Term, error) { + case *set: + return y.Map(func(term *Term) (*Term, error) { return transformTerm(t, term) }) - if err != nil { - return nil, err - } - return y, nil case *ArrayComprehension: if y.Term, err = transformTerm(t, y.Term); err != nil { return nil, err @@ -301,7 +302,7 @@ func Transform(t Transformer, x any) (any, error) { return nil, err } } - return y, nil + return orig, nil case *TemplateString: for i := range y.Parts { if expr, ok := y.Parts[i].(*Expr); ok { @@ -322,29 +323,29 @@ func Transform(t Transformer, x any) (any, error) { // TransformRefs calls the function f on all references under x. func TransformRefs(x any, f func(Ref) (Value, error)) (any, error) { - t := NewGenericTransformer(func(x any) (any, error) { + t := GenericTransformer{f: func(x any) (any, error) { if r, ok := x.(Ref); ok { return f(r) } return x, nil - }) + }} return Transform(t, x) } // TransformVars calls the function f on all vars under x. func TransformVars(x any, f func(Var) (Value, error)) (any, error) { - t := NewGenericTransformer(func(x any) (any, error) { + t := GenericTransformer{f: func(x any) (any, error) { if v, ok := x.(Var); ok { return f(v) } return x, nil - }) + }} return Transform(t, x) } // TransformComprehensions calls the function f on all comprehensions under x. func TransformComprehensions(x any, f func(any) (Value, error)) (any, error) { - t := NewGenericTransformer(func(x any) (any, error) { + t := GenericTransformer{f: func(x any) (any, error) { switch x := x.(type) { case *ArrayComprehension: return f(x) @@ -354,7 +355,7 @@ func TransformComprehensions(x any, f func(any) (Value, error)) (any, error) { return f(x) } return x, nil - }) + }} return Transform(t, x) } @@ -367,13 +368,11 @@ type GenericTransformer struct { // NewGenericTransformer returns a new GenericTransformer that will transform // AST nodes using the function f. func NewGenericTransformer(f func(x any) (any, error)) *GenericTransformer { - return &GenericTransformer{ - f: f, - } + return &GenericTransformer{f: f} } // Transform calls the function f on the GenericTransformer. -func (t *GenericTransformer) Transform(x any) (any, error) { +func (t GenericTransformer) Transform(x any) (any, error) { return t.f(x) } @@ -414,11 +413,24 @@ func transformBody(t Transformer, body Body) (Body, error) { } func transformTerm(t Transformer, term *Term) (*Term, error) { - v, err := transformValue(t, term.Value) + tv, err := transformValue(t, term.Value) if err != nil { return nil, err } - return &Term{Value: v, Location: term.Location}, nil + + // If the term was interned, make sure to return a new one instead + // of replacing the value of the interned term, as that'll be used + // elsewhere, leading to data races + if s, ok := tv.(String); ok { + if it, ok := internedStringTerms[string(s)]; ok && term == it { + return &Term{Value: tv, Location: term.Location}, nil + } + } + + // Not interned = modify the value in place + term.Value = tv + + return term, err } func transformValue(t Transformer, v Value) (Value, error) { diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/unify.go b/vendor/github.com/open-policy-agent/opa/v1/ast/unify.go index 67b89a2a93..877b576eb2 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/unify.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/unify.go @@ -115,8 +115,7 @@ func (u *unifier) unify(a *Term, b *Term) { u.markAllSafe(b) } case *SetComprehension: - switch b := b.Value.(type) { - case Var: + if b, ok := b.Value.(Var); ok { u.markSafe(b) } @@ -166,9 +165,8 @@ func (u *unifier) unify(a *Term, b *Term) { } default: - switch b := b.Value.(type) { - case Var: - u.markSafe(b) + if v, ok := b.Value.(Var); ok { + u.markSafe(v) } } } diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/visit.go b/vendor/github.com/open-policy-agent/opa/v1/ast/visit.go index c054b92b29..c291bcdc50 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/visit.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/visit.go @@ -48,6 +48,7 @@ type ( SkipWithTarget bool SkipSets bool SkipTemplateStrings bool + SkipWildcardVars bool customVisit func(vis *VarVisitor, v any) bool } @@ -191,9 +192,9 @@ func walk(v Visitor, x any) { Walk(w, t) }) case Set: - x.Foreach(func(t *Term) { + for _, t := range x.Slice() { Walk(w, t) - }) + } case *ArrayComprehension: Walk(w, x.Term) Walk(w, x.Body) @@ -425,10 +426,10 @@ func (tv *typeVisitor[T]) walk(x any, visit func(x T) bool) { tv.walk(x[i], visit) } case *object: - x.Foreach(func(k, v *Term) { - tv.walk(k, visit) - tv.walk(v, visit) - }) + for _, node := range x.sortedKeys() { + tv.walk(node.key, visit) + tv.walk(node.value, visit) + } case Object: for _, k := range x.Keys() { tv.walk(k, visit) @@ -570,10 +571,10 @@ func (vis *GenericVisitor) Walk(x any) { vis.Walk(x[i]) } case *object: - x.Foreach(func(k, _ *Term) { - vis.Walk(k) - vis.Walk(x.Get(k)) - }) + for _, node := range x.sortedKeys() { + vis.Walk(node.key) + vis.Walk(node.value) + } case Object: for _, k := range x.Keys() { vis.Walk(k) @@ -822,6 +823,13 @@ func (vis *VarVisitor) Vars() VarSet { // the visitor will _skip_ that branch of the AST func (vis *VarVisitor) visit(v any) bool { if vis.params.SkipObjectKeys { + if o, ok := v.(*object); ok { + // doesn't allocate / escape + for _, node := range o.sortedKeys() { + vis.Walk(node.value) + } + return true + } if o, ok := v.(Object); ok { o.Foreach(func(_, v *Term) { vis.Walk(v) @@ -910,6 +918,9 @@ func (vis *VarVisitor) visit(v any) bool { } } if v, ok := v.(Var); ok { + if vis.params.SkipWildcardVars && v.IsWildcard() { + return true + } vis.Add(v) return true } @@ -1032,7 +1043,7 @@ func (vis *VarVisitor) Walk(x any) { vis.Walk(x.Parts[i]) } case *Not: - vis.Walk(x.Body) + vis.WalkBody(x.Body) case *LogicalAnd: vis.WalkBody(x.Lhs) vis.WalkBody(x.Rhs) diff --git a/vendor/github.com/open-policy-agent/opa/v1/bundle/bundle.go b/vendor/github.com/open-policy-agent/opa/v1/bundle/bundle.go index c56cd122a1..289ba274dc 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/bundle/bundle.go +++ b/vendor/github.com/open-policy-agent/opa/v1/bundle/bundle.go @@ -18,6 +18,7 @@ import ( "path" "path/filepath" "reflect" + "slices" "strings" "sync" @@ -165,7 +166,7 @@ type Manifest struct { } type fileRegoVersion struct { - path glob.Glob + path *glob.Pattern version int } @@ -339,7 +340,7 @@ func (ss stringSet) Equal(other stringSet) bool { return true } -func (m *Manifest) validateAndInjectDefaults(b Bundle) error { +func (m *Manifest) validateAndInjectDefaults(b *Bundle) error { m.Init() // Validate roots in bundle. @@ -680,7 +681,7 @@ func (r *Reader) Read() (Bundle, error) { // Normalize the paths to use `/` separators path := filepath.ToSlash(f.Path()) - if strings.HasSuffix(path, RegoExt) { + if strings.HasSuffix(path, RegoExt) { //nolint: gocritic // ifElseChain fullPath := r.fullPath(path) bs := buf.Bytes() @@ -837,7 +838,7 @@ func (r *Reader) Read() (Bundle, error) { "file(s) %v specified in bundle signatures but not found in the target bundle", util.Keys(r.files)) } - if err := bundle.Manifest.validateAndInjectDefaults(*bundle); err != nil { + if err := bundle.Manifest.validateAndInjectDefaults(bundle); err != nil { return empty, err } @@ -1411,23 +1412,18 @@ func (b Bundle) Equal(other Bundle) bool { // Copy returns a deep copy of the bundle. func (b Bundle) Copy() Bundle { - // Copy data. var x any = b.Data - - if err := util.RoundTrip(&x); err != nil { + if err := util.RoundTripFast(&x); err != nil { panic(err) } - if x != nil { b.Data = x.(map[string]any) } // Copy modules. for i := range b.Modules { - bs := make([]byte, len(b.Modules[i].Raw)) - copy(bs, b.Modules[i].Raw) - b.Modules[i].Raw = bs + b.Modules[i].Raw = slices.Clone(b.Modules[i].Raw) b.Modules[i].Parsed = b.Modules[i].Parsed.Copy() } @@ -1556,12 +1552,14 @@ func MergeWithRegoVersion(bundles []*Bundle, regoVersion ast.RegoVersion, usePat return result, nil } - var roots []string - var result Bundle + var ( + roots []string + planFile string + manifestProto bool + manifestProtoSet bool + ) - var planFile string - var manifestProto bool - var manifestProtoSet bool + result := &Bundle{} for _, b := range bundles { if b.Manifest.Roots == nil { @@ -1632,7 +1630,7 @@ func MergeWithRegoVersion(bundles []*Bundle, regoVersion ast.RegoVersion, usePat return nil, err } - return &result, nil + return result, nil } func bundleRegoVersions(bundle *Bundle, regoVersion ast.RegoVersion, usePath bool) (map[string]int, error) { diff --git a/vendor/github.com/open-policy-agent/opa/v1/bundle/file.go b/vendor/github.com/open-policy-agent/opa/v1/bundle/file.go index beb2e46645..44a0a77976 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/bundle/file.go +++ b/vendor/github.com/open-policy-agent/opa/v1/bundle/file.go @@ -486,11 +486,16 @@ func NewIterator(raw []Raw) storage.Iterator { func getdepth(path string, isDir bool) int { if isDir { cleanedPath := strings.Trim(filepath.ToSlash(path), "/") - return len(strings.Split(cleanedPath, "/")) + return segmentCount(cleanedPath) } basePath := strings.Trim(filepath.Dir(filepath.ToSlash(path)), "/") - return len(strings.Split(basePath, "/")) + return segmentCount(basePath) +} + +// segmentCount avoids the []string allocation of len(strings.Split(path, "/")). +func segmentCount(path string) int { + return strings.Count(path, "/") + 1 } func getFileStoragePath(path string) (storage.Path, error) { diff --git a/vendor/github.com/open-policy-agent/opa/v1/bundle/hash.go b/vendor/github.com/open-policy-agent/opa/v1/bundle/hash.go index dd9dfe5149..66ce6a9ced 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/bundle/hash.go +++ b/vendor/github.com/open-policy-agent/opa/v1/bundle/hash.go @@ -78,7 +78,7 @@ func NewSignatureHasher(alg HashingAlgorithm) (SignatureHasher, error) { // HashFile hashes the file content, JSON or binary, both in golang native format. func (h *hasher) HashFile(v any) ([]byte, error) { hf := h.h() - walk(v, hf) + walk(v, hf, newPrimitiveEncoder()) return hf.Sum(nil), nil } @@ -91,7 +91,7 @@ func (h *hasher) HashFile(v any) ([]byte, error) { // object: Hash {, then each key (in alphabetical order) and digest of the value, then comma (between items) and finally }. // // array: Hash [, then digest of the value, then comma (between items) and finally ]. -func walk(v any, h io.Writer) { +func walk(v any, h io.Writer, pe *primitiveEncoder) { switch x := v.(type) { case map[string]any: @@ -102,9 +102,9 @@ func walk(v any, h io.Writer) { _, _ = h.Write([]byte(",")) } - _, _ = h.Write(encodePrimitive(key)) + _, _ = h.Write(pe.encode(key)) _, _ = h.Write([]byte(":")) - walk(x[key], h) + walk(x[key], h, pe) } _, _ = h.Write([]byte("}")) @@ -115,21 +115,38 @@ func walk(v any, h io.Writer) { if i > 0 { _, _ = h.Write([]byte(",")) } - walk(e, h) + walk(e, h, pe) } _, _ = h.Write([]byte("]")) case []byte: _, _ = h.Write(x) default: - _, _ = h.Write(encodePrimitive(x)) + _, _ = h.Write(pe.encode(x)) } } +// primitiveEncoder reuses its buffer across encode calls. +type primitiveEncoder struct { + buf *bytes.Buffer + enc *json.Encoder +} + +func newPrimitiveEncoder() *primitiveEncoder { + buf := new(bytes.Buffer) + enc := json.NewEncoder(buf) + enc.SetEscapeHTML(false) + return &primitiveEncoder{buf: buf, enc: enc} +} + +// encode's return value aliases the encoder's buffer and is only valid +// until the next call. +func (pe *primitiveEncoder) encode(v any) []byte { + pe.buf.Reset() + _ = pe.enc.Encode(v) + return bytes.Trim(pe.buf.Bytes(), "\n") +} + func encodePrimitive(v any) []byte { - var buf bytes.Buffer - encoder := json.NewEncoder(&buf) - encoder.SetEscapeHTML(false) - _ = encoder.Encode(v) - return bytes.Trim(buf.Bytes(), "\n") + return newPrimitiveEncoder().encode(v) } diff --git a/vendor/github.com/open-policy-agent/opa/v1/bundle/store.go b/vendor/github.com/open-policy-agent/opa/v1/bundle/store.go index c5726986c1..7d283f87bc 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/bundle/store.go +++ b/vendor/github.com/open-policy-agent/opa/v1/bundle/store.go @@ -992,60 +992,6 @@ func compileModules(compiler *ast.Compiler, m metrics.Metrics, bundles map[strin return iCompiler.VerifyAuthorizationPolicySchema(compiler, authorizationDecisionRef) } -func writeModules(ctx context.Context, store storage.Store, txn storage.Transaction, compiler *ast.Compiler, m metrics.Metrics, bundles map[string]*Bundle, extraModules map[string]*ast.Module, legacy bool, externalSources *util.HasherMap[ast.Ref, ast.ExternalRuleSource]) error { - m.Timer(metrics.RegoModuleCompile).Start() - defer m.Timer(metrics.RegoModuleCompile).Stop() - - // Apply external sources before compilation - if externalSources != nil { - externalSources.Iter(func(ref ast.Ref, source ast.ExternalRuleSource) bool { - compiler = compiler.WithExternalSource(ref, source) - return false - }) - } - - modules := map[string]*ast.Module{} - - // preserve any modules already on the compiler - maps.Copy(modules, compiler.Modules) - - // preserve any modules passed in from the store - maps.Copy(modules, extraModules) - - // include all the new bundle modules - for bundleName, b := range bundles { - if legacy { - for _, mf := range b.Modules { - modules[mf.Path] = mf.Parsed - } - } else { - maps.Copy(modules, b.ParsedModules(bundleName)) - } - } - - if compiler.Compile(modules); compiler.Failed() { - return compiler.Errors - } - for bundleName, b := range bundles { - for _, mf := range b.Modules { - var path string - - // For backwards compatibility, in legacy mode, upsert policies to - // the unprefixed path. - if legacy { - path = mf.Path - } else { - path = modulePathWithPrefix(bundleName, mf.Path) - } - - if err := store.UpsertPolicy(ctx, txn, path, mf.Raw); err != nil { - return err - } - } - } - return nil -} - func lookup(path storage.Path, data map[string]any) (any, bool) { if len(path) == 0 { return data, true diff --git a/vendor/github.com/open-policy-agent/opa/v1/format/format.go b/vendor/github.com/open-policy-agent/opa/v1/format/format.go index 860fb1a9eb..b91ba6ff25 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/format/format.go +++ b/vendor/github.com/open-policy-agent/opa/v1/format/format.go @@ -28,6 +28,7 @@ const defaultLocationFile = "__format_default__" var ( expandedConst = ast.NewBody(ast.NewExpr(ast.InternedTerm(true))) commentsSlicePool = util.NewSlicePool[*ast.Comment](50) + negativeRow = &ast.Location{Row: -1} ) // Opts lets you control the code formatting via `AstWithOpts()`. @@ -282,7 +283,7 @@ func AstWithOpts(x any, opts Opts) ([]byte, error) { switch x := x.(type) { case *ast.Module: if regoVersion == ast.RegoV1 && opts.DropV0Imports { - x.Imports = filterRegoV1Import(x.Imports) + x.Imports = slices.DeleteFunc(x.Imports, regoV1Import) } else if regoVersion == ast.RegoV0CompatV1 { x.Imports = ensureRegoV1Import(x.Imports) } @@ -509,10 +510,7 @@ func (w *writer) writeModule(module *ast.Module) error { return err } rules, others = gatherRules(others) - comments, err = w.writeRules(rules, comments) - if err != nil { - return err - } + comments = w.writeRules(rules, comments) } for i, c := range comments { @@ -593,17 +591,19 @@ func (w *writer) writeComments(comments []*ast.Comment) error { return nil } -func (w *writer) writeRules(rules []*ast.Rule, comments []*ast.Comment) ([]*ast.Comment, error) { +func (w *writer) writeRules(rules []*ast.Rule, comments []*ast.Comment) []*ast.Comment { for i, rule := range rules { var err error - comments, err = w.insertComments(comments, rule.Location) - if err != nil && !errors.As(err, &unexpectedCommentError{}) { - w.errs = append(w.errs, ast.NewError(ast.FormatErr, &ast.Location{}, "%s", err.Error())) + if comments, err = w.insertComments(comments, rule.Location); err != nil { + if _, ok := errors.AsType[unexpectedCommentError](err); !ok { + w.errs = append(w.errs, ast.NewError(ast.FormatErr, &ast.Location{}, "%s", err.Error())) + } } - comments, err = w.writeRule(rule, false, comments) - if err != nil && !errors.As(err, &unexpectedCommentError{}) { - w.errs = append(w.errs, ast.NewError(ast.FormatErr, &ast.Location{}, "%s", err.Error())) + if comments, err = w.writeRule(rule, false, comments); err != nil { + if _, ok := errors.AsType[unexpectedCommentError](err); !ok { + w.errs = append(w.errs, ast.NewError(ast.FormatErr, &ast.Location{}, "%s", err.Error())) + } } if i < len(rules)-1 && w.groupableOneLiner(rule) { @@ -616,7 +616,7 @@ func (w *writer) writeRules(rules []*ast.Rule, comments []*ast.Comment) ([]*ast. } w.blankLine() } - return comments, nil + return comments } // groupableOneLiner reports whether rule is written on a single line, and so may @@ -671,9 +671,7 @@ func (w *writer) writeRule(rule *ast.Rule, isElse bool, comments []*ast.Comment) var unexpectedComment bool comments, err = w.writeHead(rule.Head, rule.Default, isExpandedConst, comments) if err != nil { - if errors.As(err, &unexpectedCommentError{}) { - unexpectedComment = true - } else { + if unexpectedComment = isUnexpectedCommentError(err); !unexpectedComment { return nil, err } } @@ -707,7 +705,13 @@ func (w *writer) writeRule(rule *ast.Rule, isElse bool, comments []*ast.Comment) var err error comments, err = w.writeExpr(rule.Body[0], comments) if err != nil { - return nil, err + // An unexpected comment isn't fatal: the expression was + // written as-is, and the comments returned still need + // writing. Dropping them would lose every comment after + // this rule. + if _, ok := errors.AsType[unexpectedCommentError](err); !ok { + return nil, err + } } w.endLine() if rule.Else != nil { @@ -739,7 +743,7 @@ func (w *writer) writeRule(rule *ast.Rule, isElse bool, comments []*ast.Comment) comments, err = w.writeBody(rule.Body, comments) if err != nil { // the unexpected comment error is passed up to be handled by writeHead - if !errors.As(err, &unexpectedCommentError{}) { + if _, ok := errors.AsType[unexpectedCommentError](err); !ok { return nil, err } } @@ -875,10 +879,7 @@ func (w *writer) writeHead(head *ast.Head, isDefault bool, isExpandedConst bool, if len(head.Args) > 0 { w.write("(") - var args []any - for _, arg := range head.Args { - args = append(args, arg) - } + args := util.ToSliceOf[any](head.Args) var err error comments, err = w.writeIterable(args, head.Location, closingLoc(0, 0, '(', ')', head.Location), comments, w.listWriter(false)) w.write(")") @@ -976,9 +977,10 @@ func (w *writer) writeBody(body ast.Body, comments []*ast.Comment) ([]*ast.Comme } w.startLine() - comments, err = w.writeExpr(expr, comments) - if err != nil && !errors.As(err, &unexpectedCommentError{}) { - w.errs = append(w.errs, ast.NewError(ast.FormatErr, &ast.Location{}, "%s", err.Error())) + if comments, err = w.writeExpr(expr, comments); err != nil { + if _, ok := errors.AsType[unexpectedCommentError](err); !ok { + w.errs = append(w.errs, ast.NewError(ast.FormatErr, &ast.Location{}, "%s", err.Error())) + } } w.endLine() } @@ -1209,11 +1211,9 @@ func (w *writer) writeEvery(every *ast.Every, loc *ast.Location, comments []*ast } w.write(" {") comments, err = w.writeComprehensionBody('{', '}', every.Body, loc, loc, comments) - if err != nil { + if err != nil && !isUnexpectedCommentError(err) { // the unexpected comment error is passed up to be handled by writeHead - if !errors.As(err, &unexpectedCommentError{}) { - return nil, err - } + return nil, err } if len(every.Body) == 1 && @@ -1246,10 +1246,8 @@ func (w *writer) writeNot(not *ast.Not, loc *ast.Location, comments []*ast.Comme w.write("{") comments, err = w.writeComprehensionBody('{', '}', not.Body, loc, loc, comments) - if err != nil { - if !errors.As(err, &unexpectedCommentError{}) { - return nil, err - } + if err != nil && !isUnexpectedCommentError(err) { + return nil, err } if last := not.Body[len(not.Body)-1]; last.Location != nil && last.Location.Row == loc.Row { @@ -1263,10 +1261,8 @@ func (w *writer) writeNot(not *ast.Not, loc *ast.Location, comments []*ast.Comme } comments, err = w.writeExpr(not.Body[0], comments) - if err != nil { - if !errors.As(err, &unexpectedCommentError{}) { - return nil, err - } + if err != nil && !isUnexpectedCommentError(err) { + return nil, err } if parens { @@ -1345,7 +1341,7 @@ func (w *writer) writeLogical(expr *ast.Expr, comments []*ast.Comment) ([]*ast.C lhs, steps := flattenLogical(expr) comments, err := w.writeLogicalOperand(lhs, comments) - if err != nil && !errors.As(err, &unexpectedCommentError{}) { + if err != nil && !isUnexpectedCommentError(err) { return comments, err } @@ -1367,7 +1363,7 @@ func (w *writer) writeLogical(expr *ast.Expr, comments []*ast.Comment) ([]*ast.C } comments, err = w.writeLogicalOperand(s.rhs, comments) - if err != nil && !errors.As(err, &unexpectedCommentError{}) { + if err != nil && !isUnexpectedCommentError(err) { return comments, err } } @@ -1398,10 +1394,8 @@ func (w *writer) writeLogicalOperand(o logicalOperand, comments []*ast.Comment) w.write("{") comments, err := w.writeComprehensionBody('{', '}', o.body, o.brace, o.brace, comments) - if err != nil { - if !errors.As(err, &unexpectedCommentError{}) { - return comments, err - } + if err != nil && !isUnexpectedCommentError(err) { + return comments, err } if last := o.body[len(o.body)-1]; last.Location != nil && last.Location.Row == o.brace.Row { @@ -1597,7 +1591,7 @@ func (w *writer) writeFunctionCallPlain(terms []*ast.Term, comments []*ast.Comme w.write("(") defer w.write(")") - args := util.ToSliceOfAny(terms[1:]) + args := util.ToSliceOf[any](terms[1:]) loc := terms[0].Location var err error comments, err = w.writeIterable(args, loc, closingLoc(0, 0, '(', ')', loc), comments, w.listWriter(false)) @@ -1623,14 +1617,12 @@ func (w *writer) writeWith(with *ast.With, comments []*ast.Comment, indented boo } w.write(" as ") comments, err = w.writeTerm(with.Value, comments) - if err != nil { + if err != nil && !isUnexpectedCommentError(err) { // An unexpectedCommentError from writeTerm signals that it fell // back to writing the term's original unformatted text — the value // was written successfully, so don't abort the surrounding chain // of `with` clauses (issue #8765). - if !errors.As(err, &unexpectedCommentError{}) { - return comments, err - } + return comments, err } return comments, nil } @@ -1659,7 +1651,7 @@ func (w *writer) writeTerm(term *ast.Term, comments []*ast.Comment) ([]*ast.Comm comments, err := w.writeTermParens(false, term, comments) if err != nil { - if errors.As(err, &unexpectedCommentError{}) { + if isUnexpectedCommentError(err) { w.buf.Truncate(currentLen) w.level = currentLevel @@ -1921,7 +1913,7 @@ func (w *writer) writeRef(x ast.Ref, comments []*ast.Comment) ([]*ast.Comment, e w.write("[") comments, err = w.writeTerm(t, comments) if err != nil { - if errors.As(err, &unexpectedCommentError{}) { + if _, ok := errors.AsType[unexpectedCommentError](err); ok { // add a new line so that the closing bracket isn't part of the unexpected comment w.write("\n") } else { @@ -2071,7 +2063,7 @@ func (w *writer) writeObject(obj ast.Object, loc *ast.Location, comments []*ast. w.write("{") defer w.write("}") - var s []any + s := make([]any, 0, obj.Len()) obj.Foreach(func(k, v *ast.Term) { s = append(s, ast.Item(k, v)) }) @@ -2082,7 +2074,7 @@ func (w *writer) writeArray(arr *ast.Array, loc *ast.Location, comments []*ast.C w.write("[") defer w.write("]") - var s []any + s := make([]any, 0, arr.Len()) arr.Foreach(func(t *ast.Term) { s = append(s, t) }) @@ -2095,10 +2087,9 @@ func (w *writer) writeArray(arr *ast.Array, loc *ast.Location, comments []*ast.C } func (w *writer) writeSet(set ast.Set, loc *ast.Location, comments []*ast.Comment) ([]*ast.Comment, error) { - + var err error if set.Len() == 0 { w.write("set()") - var err error comments, err = w.insertComments(comments, closingLoc(0, 0, '(', ')', loc)) if err != nil { return nil, err @@ -2109,11 +2100,7 @@ func (w *writer) writeSet(set ast.Set, loc *ast.Location, comments []*ast.Commen w.write("{") defer w.write("}") - var s []any - set.Foreach(func(t *ast.Term) { - s = append(s, t) - }) - var err error + s := util.ToSliceOf[any](set.Slice()) comments, err = w.writeIterable(s, loc, closingLoc(0, 0, '{', '}', loc), comments, w.listWriter(true)) if err != nil { return nil, err @@ -2188,7 +2175,7 @@ func (w *writer) writeComprehension(openChar, closeChar byte, term *ast.Term, bo } func (w *writer) writeComprehensionBody(openChar, closeChar byte, body ast.Body, term, compr *ast.Location, comments []*ast.Comment) ([]*ast.Comment, error) { - lines, err := w.groupIterable(util.ToSliceOfAny(body), term) + lines, err := w.groupIterable(util.ToSliceOf[any](body), term) if err != nil { return nil, err } @@ -2755,8 +2742,6 @@ func getLocs(a, b any) (*ast.Location, *ast.Location, error) { return al, bl, errors.Join(err1, err2) } -var negativeRow = &ast.Location{Row: -1} - func closingLoc(skipOpen, skipClose, openChar, closeChar byte, loc *ast.Location) *ast.Location { i, offset := 0, 0 @@ -2945,10 +2930,9 @@ func ensureFutureKeywordImport(imps []*ast.Import, kw string) []*ast.Import { return imps } } - imp := &ast.Import{ - Path: ast.MustParseTerm("future.keywords." + kw), - } + imp := &ast.Import{Path: ast.MustParseTerm("future.keywords." + kw)} imp.Location = nextImportLoc(imps, imp) + return append(imps, imp) } @@ -2958,7 +2942,7 @@ func nextImportLoc(imps []*ast.Import, node ast.Node) *ast.Location { if imp.Loc() == nil { continue } - if isFutureKeywordsImport(imp) || isRegoV1Compatible(imp) { + if imp.Path.Value.(ast.Ref).HasPrefix(ast.FutureKeywordsRef[:1]) || isRegoV1Compatible(imp) { if imp.Loc().Row > maxRow { maxRow = imp.Loc().Row } @@ -2970,11 +2954,6 @@ func nextImportLoc(imps []*ast.Import, node ast.Node) *ast.Location { return ast.NewLocation([]byte(node.String()), defaultLocationFile, maxRow+1, 1) } -func isFutureKeywordsImport(imp *ast.Import) bool { - path := imp.Path.Value.(ast.Ref) - return len(path) >= 2 && ast.FutureRootDocument.Equal(path[0]) -} - func isAddedImport(imp *ast.Import) bool { return imp.Loc() != nil && imp.Loc().File == defaultLocationFile } @@ -3002,34 +2981,21 @@ func addedImportFollowsRule(others []any) bool { } func ensureRegoV1Import(imps []*ast.Import) []*ast.Import { - return ensureImport(imps, ast.RegoV1CompatibleRef) -} - -func filterRegoV1Import(imps []*ast.Import) []*ast.Import { - var ret []*ast.Import - for _, imp := range imps { - path := imp.Path.Value.(ast.Ref) - if !ast.RegoV1CompatibleRef.Equal(path) { - ret = append(ret, imp) - } - } - return ret -} - -func ensureImport(imps []*ast.Import, path ast.Ref) []*ast.Import { for _, imp := range imps { - p := imp.Path.Value.(ast.Ref) - if p.Equal(path) { + if ast.RegoV1CompatibleRef.Equal(imp.Path.Value) { return imps } } - imp := &ast.Import{ - Path: ast.NewTerm(path), - } + imp := &ast.Import{Path: ast.NewTerm(ast.RegoV1CompatibleRef)} imp.Location = nextImportLoc(imps, imp) + return append(imps, imp) } +func regoV1Import(imp *ast.Import) bool { + return ast.RegoV1CompatibleRef.Equal(imp.Path.Value) +} + // ArityFormatErrDetail but for `fmt` checks since compiler has not run yet. type ArityFormatErrDetail struct { Have []string `json:"have"` @@ -3070,3 +3036,8 @@ func isRegoV1Compatible(imp *ast.Import) bool { ast.RegoRootDocument.Equal(path[0]) && path[1].Equal(ast.InternedTerm("v1")) } + +func isUnexpectedCommentError(err error) bool { + _, ok := errors.AsType[unexpectedCommentError](err) + return ok +} diff --git a/vendor/github.com/open-policy-agent/opa/v1/loader/errors.go b/vendor/github.com/open-policy-agent/opa/v1/loader/errors.go index 55b8e7dc44..3e3fb732d9 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/loader/errors.go +++ b/vendor/github.com/open-policy-agent/opa/v1/loader/errors.go @@ -9,6 +9,7 @@ import ( "strings" "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/util" ) // Errors is a wrapper for multiple loader errors. @@ -21,10 +22,7 @@ func (e Errors) Error() string { if len(e) == 1 { return "1 error occurred during loading: " + e[0].Error() } - buf := make([]string, len(e)) - for i := range buf { - buf[i] = e[i].Error() - } + buf := util.Map(e, error.Error) return fmt.Sprintf("%v errors occurred during loading:\n", len(e)) + strings.Join(buf, "\n") } diff --git a/vendor/github.com/open-policy-agent/opa/v1/loader/loader.go b/vendor/github.com/open-policy-agent/opa/v1/loader/loader.go index f78f9fb1d2..81eb9a7eed 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/loader/loader.go +++ b/vendor/github.com/open-policy-agent/opa/v1/loader/loader.go @@ -15,10 +15,9 @@ import ( "runtime" "strings" - "sigs.k8s.io/yaml" - fileurl "github.com/open-policy-agent/opa/internal/file/url" "github.com/open-policy-agent/opa/internal/merge" + "github.com/open-policy-agent/opa/internal/yaml" "github.com/open-policy-agent/opa/v1/ast" astJSON "github.com/open-policy-agent/opa/v1/ast/json" "github.com/open-policy-agent/opa/v1/bundle" diff --git a/vendor/github.com/open-policy-agent/opa/v1/logging/logging.go b/vendor/github.com/open-policy-agent/opa/v1/logging/logging.go index 03d5f33c64..457ee027a7 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/logging/logging.go +++ b/vendor/github.com/open-policy-agent/opa/v1/logging/logging.go @@ -359,7 +359,7 @@ func (h *SlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler { } } -func (h *SlogHandler) WithGroup(name string) slog.Handler { +func (h *SlogHandler) WithGroup(string) slog.Handler { return h } diff --git a/vendor/github.com/open-policy-agent/opa/v1/metrics/metrics.go b/vendor/github.com/open-policy-agent/opa/v1/metrics/metrics.go index 119af78aab..cc64665425 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/metrics/metrics.go +++ b/vendor/github.com/open-policy-agent/opa/v1/metrics/metrics.go @@ -346,12 +346,12 @@ var ( noOpCounterInstance = &noOpCounter{} ) -func (*noOpMetrics) Info() Info { return Info{Name: ""} } -func (*noOpMetrics) Timer(name string) Timer { return noOpTimerInstance } -func (*noOpMetrics) Histogram(name string) Histogram { return noOpHistogramInstance } -func (*noOpMetrics) Counter(name string) Counter { return noOpCounterInstance } -func (*noOpMetrics) All() map[string]any { return nil } -func (*noOpMetrics) Clear() {} +func (*noOpMetrics) Info() Info { return Info{Name: ""} } +func (*noOpMetrics) Timer(string) Timer { return noOpTimerInstance } +func (*noOpMetrics) Histogram(string) Histogram { return noOpHistogramInstance } +func (*noOpMetrics) Counter(string) Counter { return noOpCounterInstance } +func (*noOpMetrics) All() map[string]any { return nil } +func (*noOpMetrics) Clear() {} func (*noOpMetrics) MarshalJSON() ([]byte, error) { return []byte(`{"name": ""}`), nil } @@ -361,10 +361,10 @@ func (*noOpTimer) Stop() int64 { return 0 } func (*noOpTimer) Value() any { return 0 } func (*noOpTimer) Int64() int64 { return 0 } -func (*noOpHistogram) Update(v int64) {} -func (*noOpHistogram) Value() any { return nil } +func (*noOpHistogram) Update(int64) {} +func (*noOpHistogram) Value() any { return nil } func (*noOpCounter) Incr() {} -func (*noOpCounter) Add(_ uint64) {} +func (*noOpCounter) Add(uint64) {} func (*noOpCounter) Value() any { return 0 } func (*noOpCounter) Int64() int64 { return 0 } diff --git a/vendor/github.com/open-policy-agent/opa/v1/rego/rego.go b/vendor/github.com/open-policy-agent/opa/v1/rego/rego.go index fa7d5ed349..3a2ff89ab4 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/rego/rego.go +++ b/vendor/github.com/open-policy-agent/opa/v1/rego/rego.go @@ -7,6 +7,7 @@ package rego import ( "bytes" + "cmp" "context" "errors" "fmt" @@ -479,6 +480,11 @@ func (pq preparedQuery) Modules() map[string]*ast.Module { // once the evaluation is complete to close any transactions that might have // been opened. func (pq preparedQuery) newEvalContext(ctx context.Context, options []EvalOption) (*EvalContext, func(context.Context), error) { + disableInlining, err := parseStringsToRefs(pq.r.disableInlining) + if err != nil { + return nil, func(context.Context) {}, err + } + ectx := &EvalContext{ hasInput: false, rawInput: nil, @@ -491,6 +497,7 @@ func (pq preparedQuery) newEvalContext(ctx context.Context, options []EvalOption queryTracers: nil, unknowns: pq.r.unknowns, parsedUnknowns: pq.r.parsedUnknowns, + disableInlining: disableInlining, nondeterministicBuiltins: pq.r.nondeterministicBuiltins, compiledQuery: compiledQuery{}, indexing: true, @@ -516,12 +523,6 @@ func (pq preparedQuery) newEvalContext(ctx context.Context, options []EvalOption // Default to an empty "finish" function finishFunc := func(context.Context) {} - var err error - ectx.disableInlining, err = parseStringsToRefs(pq.r.disableInlining) - if err != nil { - return nil, finishFunc, err - } - if ectx.txn == nil { ectx.txn, err = pq.r.store.NewTransaction(ctx) if err != nil { @@ -704,6 +705,7 @@ type Rego struct { interQueryBuiltinValueCache cache.InterQueryValueCache ndBuiltinCache builtins.NDBCache strictBuiltinErrors bool + stackTraces bool builtinErrorList *[]topdown.Error resolvers []refResolver externalSources []ast.ExternalRuleSource @@ -767,7 +769,7 @@ func RegisterBuiltin1(decl *Function, impl Builtin1) { }) topdown.RegisterBuiltinFunc(decl.Name, func(bctx BuiltinContext, terms []*ast.Term, iter func(*ast.Term) error) error { result, err := memoize(decl, bctx, terms, func() (*ast.Term, error) { return impl(bctx, terms[0]) }) - return finishFunction(decl.Name, bctx, result, err, iter) + return finishFunction(decl.Name, bctx.Location, result, err, iter) }) } @@ -781,7 +783,7 @@ func RegisterBuiltin2(decl *Function, impl Builtin2) { }) topdown.RegisterBuiltinFunc(decl.Name, func(bctx BuiltinContext, terms []*ast.Term, iter func(*ast.Term) error) error { result, err := memoize(decl, bctx, terms, func() (*ast.Term, error) { return impl(bctx, terms[0], terms[1]) }) - return finishFunction(decl.Name, bctx, result, err, iter) + return finishFunction(decl.Name, bctx.Location, result, err, iter) }) } @@ -795,7 +797,7 @@ func RegisterBuiltin3(decl *Function, impl Builtin3) { }) topdown.RegisterBuiltinFunc(decl.Name, func(bctx BuiltinContext, terms []*ast.Term, iter func(*ast.Term) error) error { result, err := memoize(decl, bctx, terms, func() (*ast.Term, error) { return impl(bctx, terms[0], terms[1], terms[2]) }) - return finishFunction(decl.Name, bctx, result, err, iter) + return finishFunction(decl.Name, bctx.Location, result, err, iter) }) } @@ -809,7 +811,7 @@ func RegisterBuiltin4(decl *Function, impl Builtin4) { }) topdown.RegisterBuiltinFunc(decl.Name, func(bctx BuiltinContext, terms []*ast.Term, iter func(*ast.Term) error) error { result, err := memoize(decl, bctx, terms, func() (*ast.Term, error) { return impl(bctx, terms[0], terms[1], terms[2], terms[3]) }) - return finishFunction(decl.Name, bctx, result, err, iter) + return finishFunction(decl.Name, bctx.Location, result, err, iter) }) } @@ -823,7 +825,7 @@ func RegisterBuiltinDyn(decl *Function, impl BuiltinDyn) { }) topdown.RegisterBuiltinFunc(decl.Name, func(bctx BuiltinContext, terms []*ast.Term, iter func(*ast.Term) error) error { result, err := memoize(decl, bctx, terms, func() (*ast.Term, error) { return impl(bctx, terms) }) - return finishFunction(decl.Name, bctx, result, err, iter) + return finishFunction(decl.Name, bctx.Location, result, err, iter) }) } @@ -831,7 +833,7 @@ func RegisterBuiltinDyn(decl *Function, impl BuiltinDyn) { func Function1(decl *Function, f Builtin1) func(*Rego) { return newFunction(decl, func(bctx BuiltinContext, terms []*ast.Term, iter func(*ast.Term) error) error { result, err := memoize(decl, bctx, terms, func() (*ast.Term, error) { return f(bctx, terms[0]) }) - return finishFunction(decl.Name, bctx, result, err, iter) + return finishFunction(decl.Name, bctx.Location, result, err, iter) }) } @@ -839,7 +841,7 @@ func Function1(decl *Function, f Builtin1) func(*Rego) { func Function2(decl *Function, f Builtin2) func(*Rego) { return newFunction(decl, func(bctx BuiltinContext, terms []*ast.Term, iter func(*ast.Term) error) error { result, err := memoize(decl, bctx, terms, func() (*ast.Term, error) { return f(bctx, terms[0], terms[1]) }) - return finishFunction(decl.Name, bctx, result, err, iter) + return finishFunction(decl.Name, bctx.Location, result, err, iter) }) } @@ -847,7 +849,7 @@ func Function2(decl *Function, f Builtin2) func(*Rego) { func Function3(decl *Function, f Builtin3) func(*Rego) { return newFunction(decl, func(bctx BuiltinContext, terms []*ast.Term, iter func(*ast.Term) error) error { result, err := memoize(decl, bctx, terms, func() (*ast.Term, error) { return f(bctx, terms[0], terms[1], terms[2]) }) - return finishFunction(decl.Name, bctx, result, err, iter) + return finishFunction(decl.Name, bctx.Location, result, err, iter) }) } @@ -855,7 +857,7 @@ func Function3(decl *Function, f Builtin3) func(*Rego) { func Function4(decl *Function, f Builtin4) func(*Rego) { return newFunction(decl, func(bctx BuiltinContext, terms []*ast.Term, iter func(*ast.Term) error) error { result, err := memoize(decl, bctx, terms, func() (*ast.Term, error) { return f(bctx, terms[0], terms[1], terms[2], terms[3]) }) - return finishFunction(decl.Name, bctx, result, err, iter) + return finishFunction(decl.Name, bctx.Location, result, err, iter) }) } @@ -863,7 +865,7 @@ func Function4(decl *Function, f Builtin4) func(*Rego) { func FunctionDyn(decl *Function, f BuiltinDyn) func(*Rego) { return newFunction(decl, func(bctx BuiltinContext, terms []*ast.Term, iter func(*ast.Term) error) error { result, err := memoize(decl, bctx, terms, func() (*ast.Term, error) { return f(bctx, terms) }) - return finishFunction(decl.Name, bctx, result, err, iter) + return finishFunction(decl.Name, bctx.Location, result, err, iter) }) } @@ -1308,6 +1310,16 @@ func StrictBuiltinErrors(yes bool) func(r *Rego) { } } +// StackTraces tells the evaluator to record the stack of queries being evaluated +// when an error occurred on the returned *topdown.Error. The stack is exposed as +// topdown.Error.StackTrace and left out of the error message, so callers render +// it themselves. Off by default; see [topdown.Query.WithStackTraces] for why. +func StackTraces(yes bool) func(r *Rego) { + return func(r *Rego) { + r.stackTraces = yes + } +} + // BuiltinErrorList supplies an error slice to store built-in function errors. func BuiltinErrorList(list *[]topdown.Error) func(r *Rego) { return func(r *Rego) { @@ -1608,8 +1620,10 @@ func (r *Rego) Partial(ctx context.Context) (*PartialQueries, error) { EvalTransaction(r.txn), EvalMetrics(r.metrics), EvalInstrument(r.instrument), + EvalTime(r.time), EvalInterQueryBuiltinCache(r.interQueryBuiltinCache), EvalInterQueryBuiltinValueCache(r.interQueryBuiltinValueCache), + EvalSeed(r.seed), } if r.ndBuiltinCache != nil { @@ -1982,7 +1996,7 @@ func (r *Rego) prepare(ctx context.Context, qType queryType, extras []extraStage var queryImports []*ast.Import for _, imp := range imports { path := imp.Path.Value.(ast.Ref) - if path.HasPrefix([]*ast.Term{ast.FutureRootDocument}) || path.HasPrefix([]*ast.Term{ast.RegoRootDocument}) { + if path.HasPrefix(ast.FutureKeywordsRef[:1]) || path.HasPrefix(ast.RegoV1CompatibleRef[:1]) { queryImports = append(queryImports, imp) } } @@ -2137,7 +2151,7 @@ func (*Rego) parseRawInput(rawInput *any, m metrics.Metrics) (ast.Value, error) // roundtrip through json: this turns slices (e.g. []string, []bool) into // []any, the only array type ast.InterfaceToValue can work with - if err := util.RoundTrip(rawPtr); err != nil { + if err := util.RoundTripFast(rawPtr); err != nil { return nil, err } @@ -2157,24 +2171,21 @@ func (r *Rego) parseQuery(queryImports []*ast.Import, m metrics.Metrics) (ast.Bo return nil, err } popts.RegoVersion = r.regoVersion - popts, err = parserOptionsFromRegoVersionImport(queryImports, popts) - if err != nil { - return nil, err - } + popts = parserOptionsFromRegoVersionImport(queryImports, popts) popts.SkipRules = true popts.Capabilities = r.capabilities return ast.ParseBodyWithOpts(r.query, popts) } -func parserOptionsFromRegoVersionImport(imports []*ast.Import, popts ast.ParserOptions) (ast.ParserOptions, error) { +func parserOptionsFromRegoVersionImport(imports []*ast.Import, popts ast.ParserOptions) ast.ParserOptions { for _, imp := range imports { - if ast.RegoV1CompatibleRef.Compare(imp.Path.Value) == 0 { + if ast.RegoV1CompatibleRef.Equal(imp.Path.Value) { popts.RegoVersion = ast.RegoV1 - return popts, nil + return popts } } - return popts, nil + return popts } func (r *Rego) compileModules(ctx context.Context, txn storage.Transaction, m metrics.Metrics) error { @@ -2336,6 +2347,7 @@ func (r *Rego) eval(ctx context.Context, ectx *EvalContext) (ResultSet, error) { WithInterQueryBuiltinCache(ectx.interQueryBuiltinCache). WithInterQueryBuiltinValueCache(ectx.interQueryBuiltinValueCache). WithStrictBuiltinErrors(r.strictBuiltinErrors). + WithStackTraces(r.stackTraces). WithBuiltinErrorList(ectx.builtinErrorList). WithSeed(ectx.seed). WithPrintHook(ectx.printHook). @@ -2343,13 +2355,8 @@ func (r *Rego) eval(ctx context.Context, ectx *EvalContext) (ResultSet, error) { WithVirtualCache(ectx.virtualCache). WithBaseCache(ectx.baseCache). WithRequestMetadata(ectx.requestMetadata). - WithResponseMetadata(ectx.responseMetadata) - - if ectx.evaluated != nil { - q = q.WithEvaluatedRuleTracker(ectx.evaluated) - } else { - q = q.WithEvaluatedRuleTracker(r.evaluated) - } + WithResponseMetadata(ectx.responseMetadata). + WithEvaluatedRuleTracker(cmp.Or(ectx.evaluated, r.evaluated)) if !ectx.time.IsZero() { q = q.WithTime(ectx.time) @@ -2647,10 +2654,12 @@ func (r *Rego) partial(ctx context.Context, ectx *EvalContext) (*PartialQueries, WithInterQueryBuiltinCache(ectx.interQueryBuiltinCache). WithInterQueryBuiltinValueCache(ectx.interQueryBuiltinValueCache). WithStrictBuiltinErrors(ectx.strictBuiltinErrors). + WithStackTraces(r.stackTraces). WithSeed(ectx.seed). WithPrintHook(ectx.printHook). WithRequestMetadata(ectx.requestMetadata). - WithResponseMetadata(ectx.responseMetadata) + WithResponseMetadata(ectx.responseMetadata). + WithEvaluatedRuleTracker(cmp.Or(ectx.evaluated, r.evaluated)) if !ectx.time.IsZero() { q = q.WithTime(ectx.time) @@ -2841,11 +2850,11 @@ func (r *Rego) generateTermVar() *ast.Term { return ast.VarTerm(fmt.Sprintf("%sterm%v", prefix, r.termVarID)) } -func (r Rego) hasQuery() bool { +func (r *Rego) hasQuery() bool { return len(r.query) != 0 || len(r.parsedQuery) != 0 } -func (r Rego) hasWasmModule() bool { +func (r *Rego) hasWasmModule() bool { for _, b := range r.bundles { if len(b.WasmModules) > 0 { return true @@ -2979,11 +2988,9 @@ func iteration(x any) bool { } case ast.Ref: if !stopped { - if bi := ast.BuiltinMap[x.String()]; bi != nil { - if bi.Relation { - stopped = true - return stopped - } + if bi := ast.BuiltinMap[x.String()]; bi != nil && bi.Relation { + stopped = true + return stopped } for i := 1; i < len(x); i++ { if _, ok := x[i].Value.(ast.Var); ok { @@ -3006,23 +3013,13 @@ func parseStringsToRefs(s []string) ([]ast.Ref, error) { if len(s) == 0 { return nil, nil } - - refs := make([]ast.Ref, len(s)) - for i := range refs { - var err error - refs[i], err = ast.ParseRef(s[i]) - if err != nil { - return nil, err - } - } - - return refs, nil + return util.TryMap(s, ast.ParseRef) } // helper function to finish a built-in function call. If an error occurred, // wrap the error and return it. Otherwise, invoke the iterator if the result // was defined. -func finishFunction(name string, bctx topdown.BuiltinContext, result *ast.Term, err error, iter func(*ast.Term) error) error { +func finishFunction(name string, loc *ast.Location, result *ast.Term, err error, iter func(*ast.Term) error) error { if err != nil { sb := strings.Builder{} if e, ok := errors.AsType[*HaltError](err); ok { @@ -3033,7 +3030,7 @@ func finishFunction(name string, bctx topdown.BuiltinContext, result *ast.Term, tdErr := &topdown.Error{ Code: topdown.BuiltinErr, Message: sb.String(), - Location: bctx.Location, + Location: loc, } return topdown.Halt{Err: tdErr.Wrap(e)} } @@ -3044,7 +3041,7 @@ func finishFunction(name string, bctx topdown.BuiltinContext, result *ast.Term, tdErr := &topdown.Error{ Code: topdown.BuiltinErr, Message: sb.String(), - Location: bctx.Location, + Location: loc, } return tdErr.Wrap(err) } @@ -3070,11 +3067,10 @@ func newFunction(decl *Function, f topdown.BuiltinFunc) func(*Rego) { } func generateJSON(term *ast.Term, ectx *EvalContext) (any, error) { - return ast.JSONWithOpt(term.Value, - ast.JSONOpt{ - SortSets: ectx.sortSets, - CopyMaps: ectx.copyMaps, - }) + return ast.JSONWithOpt(term.Value, ast.JSONOpt{ + SortSets: ectx.sortSets, + CopyMaps: ectx.copyMaps, + }) } func (r *Rego) planQuery(queries []ast.Body, evalQueryType queryType) (*ir.Policy, error) { diff --git a/vendor/github.com/open-policy-agent/opa/v1/storage/errors.go b/vendor/github.com/open-policy-agent/opa/v1/storage/errors.go index 95bf25a156..0781eec4ec 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/storage/errors.go +++ b/vendor/github.com/open-policy-agent/opa/v1/storage/errors.go @@ -52,37 +52,22 @@ func (err *Error) Error() string { // IsNotFound returns true if this error is a NotFoundErr. func IsNotFound(err error) bool { - if err, ok := err.(*Error); ok { - return err.Code == NotFoundErr - } - return false + return isError(err, NotFoundErr) } // IsWriteConflictError returns true if this error a WriteConflictErr. func IsWriteConflictError(err error) bool { - switch err := err.(type) { - case *Error: - return err.Code == WriteConflictErr - } - return false + return isError(err, WriteConflictErr) } // IsInvalidPatch returns true if this error is a InvalidPatchErr. func IsInvalidPatch(err error) bool { - switch err := err.(type) { - case *Error: - return err.Code == InvalidPatchErr - } - return false + return isError(err, InvalidPatchErr) } // IsInvalidTransaction returns true if this error is a InvalidTransactionErr. func IsInvalidTransaction(err error) bool { - switch err := err.(type) { - case *Error: - return err.Code == InvalidTransactionErr - } - return false + return isError(err, InvalidTransactionErr) } // IsIndexingNotSupported is a stub for backwards-compatibility. @@ -115,3 +100,8 @@ func policyNotSupportedError() *Error { Code: PolicyNotSupportedErr, } } + +func isError(err error, code string) bool { + e, ok := err.(*Error) + return ok && e.Code == code +} diff --git a/vendor/github.com/open-policy-agent/opa/v1/storage/inmem/ast.go b/vendor/github.com/open-policy-agent/opa/v1/storage/inmem/ast.go index 40f18ab0de..3f7e93d593 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/storage/inmem/ast.go +++ b/vendor/github.com/open-policy-agent/opa/v1/storage/inmem/ast.go @@ -284,21 +284,19 @@ func removeInAstArray(arr *ast.Array, path storage.Path) (ast.Value, error) { } if len(path) == 1 { - var elems []*ast.Term // Note: possibly expensive operation for large data. + elems := make([]*ast.Term, 0, arr.Len()-1) for i := range arr.Len() { - if i == idx { - continue + if i != idx { + elems = append(elems, arr.Elem(i)) } - elems = append(elems, arr.Elem(i)) } return ast.NewArray(elems...), nil } updatedChild, err := removeInAst(arr.Elem(idx).Value, path[1:]) - if err != nil { - return nil, err + if err == nil { + arr.Set(idx, ast.NewTerm(updatedChild)) } - arr.Set(idx, ast.NewTerm(updatedChild)) - return arr, nil + return arr, err } diff --git a/vendor/github.com/open-policy-agent/opa/v1/storage/inmem/inmem.go b/vendor/github.com/open-policy-agent/opa/v1/storage/inmem/inmem.go index 77bfada098..d4b2d06d01 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/storage/inmem/inmem.go +++ b/vendor/github.com/open-policy-agent/opa/v1/storage/inmem/inmem.go @@ -345,7 +345,7 @@ func (db *store) Write(_ context.Context, txn storage.Transaction, op storage.Pa val := util.Reference(value) if db.roundTripOnWrite { - if err := util.RoundTrip(val); err != nil { + if err := util.RoundTripFast(val); err != nil { return err } } diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/bindings.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/bindings.go index 809a31676c..6ad36c8fff 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/bindings.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/bindings.go @@ -6,10 +6,10 @@ package topdown import ( "fmt" - "strconv" "strings" "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/util" ) type undo struct { @@ -35,9 +35,9 @@ type bindings struct { instr *Instrumentation } -func newBindings(id uint64, instr *Instrumentation) *bindings { +func newBindings(instr *Instrumentation) *bindings { values := newBindingsArrayHashmap() - return &bindings{id, values, instr} + return &bindings{0, values, instr} } // newBindingsWithSize creates bindings pre-sized for the expected number of entries. @@ -49,7 +49,6 @@ func newBindingsWithSize(id uint64, instr *Instrumentation, sizeHint int) *bindi } func (u *bindings) Iter(caller *bindings, iter func(*ast.Term, *ast.Term) error) error { - var err error u.values.Iter(func(k *ast.Term, _ value) bool { @@ -87,6 +86,10 @@ func (u *bindings) PlugNamespaced(a *ast.Term, caller *bindings) *ast.Term { return u.plugNamespaced(a, caller) } +func (u *bindings) size() int { + return u.values.size() +} + func (u *bindings) plugNamespaced(a *ast.Term, caller *bindings) *ast.Term { switch v := a.Value.(type) { case ast.Var: @@ -189,12 +192,12 @@ func (u *bindings) namespaceVar(v *ast.Term, caller *bindings) *ast.Term { if !ok { panic("illegal value") } - if caller != nil && caller != u { - // Root documents (i.e., data, input) should never be namespaced because they - // are globally unique. - if !ast.RootDocumentNames.Contains(v) { - return ast.VarTerm(string(name) + strconv.FormatUint(u.id, 10)) - } + if caller != nil && caller != u && !ast.RootDocumentNames.Contains(v) { + // Root documents (i.e., data, input) should never be namespaced + // because they are globally unique. + len := len(name) + util.NumDigitsUint(u.id) + buf := util.AppendInt(append(make([]byte, 0, len), name...), u.id) + return ast.VarTerm(util.ByteSliceToString(buf)) } return v } @@ -296,11 +299,7 @@ func (vis namespacingVisitor) namespaceTerm(a *ast.Term) *ast.Term { return &cpy case ast.Ref: cpy := *a - ref := make(ast.Ref, len(v)) - for i := range ref { - ref[i] = vis.namespaceTerm(v[i]) - } - cpy.Value = ref + cpy.Value = ast.Ref(util.Map(v, vis.namespaceTerm)) return &cpy } return a @@ -458,6 +457,13 @@ func (b *bindingsArrayHashmap) Iter(f func(k *ast.Term, v value) bool) { } } +func (b *bindingsArrayHashmap) size() int { + if b.m == nil { + return b.n + } + return len(b.m) +} + func (b *bindingsArrayHashmap) find(key *ast.Term) int { if b.a == nil || b.n == 0 { return -1 diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/cache.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/cache.go index e9bb12ab00..a7022f1703 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/cache.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/cache.go @@ -144,7 +144,7 @@ type baseCache struct { root *baseCacheElem } -func newBaseCache() *baseCache { +func newBaseCache() BaseCache { return &baseCache{ root: newBaseCacheElem(), } diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/copypropagation/copypropagation.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/copypropagation/copypropagation.go index e086e5d418..75cc6dbb12 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/copypropagation/copypropagation.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/copypropagation/copypropagation.go @@ -77,9 +77,6 @@ func (p *CopyPropagator) WithCompiler(c *ast.Compiler) *CopyPropagator { // Apply executes the copy propagation optimization and returns a new query. func (p *CopyPropagator) Apply(query ast.Body) ast.Body { - - result := ast.NewBody() - uf, ok := makeDisjointSets(p.livevars, query) if !ok { return query @@ -102,6 +99,7 @@ func (p *CopyPropagator) Apply(query ast.Body) ast.Body { return false }) + result := ast.NewBody() removedEqs := ast.NewValueMap() for _, expr := range query { diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/encoding.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/encoding.go index 5ed8df68f3..5e636760f3 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/encoding.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/encoding.go @@ -12,8 +12,7 @@ import ( "net/url" "strings" - "sigs.k8s.io/yaml" - + "github.com/open-policy-agent/opa/internal/yaml" "github.com/open-policy-agent/opa/v1/ast" "github.com/open-policy-agent/opa/v1/topdown/builtins" "github.com/open-policy-agent/opa/v1/util" diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/errors.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/errors.go index e2dc597ace..43a2fd72df 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/errors.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/errors.go @@ -12,6 +12,8 @@ import ( "github.com/open-policy-agent/opa/v1/util" ) +var cancelErr = &Error{Code: CancelErr} + // Halt is a special error type that built-in function implementations return to indicate // that policy evaluation should stop immediately. type Halt struct { @@ -30,11 +32,16 @@ type Error struct { Code string `json:"code"` Message string `json:"message"` Location *ast.Location `json:"location,omitempty"` - err error `json:"-"` + + // StackTrace is the stack of queries being evaluated when the error occurred. + // Only populated when enabled (see Query.WithStackTraces), and left out of + // Error() so enabling it doesn't change the messages callers display. + StackTrace StackTrace `json:"stack_trace,omitempty"` + + err error `json:"-"` } const ( - // InternalErr represents an unknown evaluation error. InternalErr string = "eval_internal_error" @@ -62,13 +69,13 @@ const ( // IsError returns true if the err is an Error. func IsError(err error) bool { - var e *Error - return errors.As(err, &e) + _, ok := errors.AsType[*Error](err) + return ok } // IsCancel returns true if err was caused by cancellation. func IsCancel(err error) bool { - return errors.Is(err, &Error{Code: CancelErr}) + return errors.Is(err, cancelErr) } // Is allows matching topdown errors using errors.Is (see IsCancel). @@ -76,7 +83,7 @@ func (e *Error) Is(target error) bool { if t, ok := errors.AsType[*Error](target); ok { return (t.Code == "" || e.Code == t.Code) && (t.Message == "" || e.Message == t.Message) && - (t.Location == nil || t.Location.Compare(e.Location) == 0) + (t.Location == nil || t.Location.Equal(e.Location)) } return false } diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/errors_jsonv2.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/errors_jsonv2.go index 29b6f3761e..1d0ed6d72a 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/errors_jsonv2.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/errors_jsonv2.go @@ -20,5 +20,9 @@ func (e *Error) MarshalJSONTo(enc *jsontext.Encoder) (err error) { err = jsonv2.WriteField(enc, "location", e.Location) } + if len(e.StackTrace) > 0 { + err = errors.Join(err, jsonv2.WriteFieldValue(enc, "stack_trace", e.StackTrace)) + } + return errors.Join(err, enc.WriteToken(jsontext.EndObject)) } diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/eval.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/eval.go index 457bd38ba8..882e248501 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/eval.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/eval.go @@ -107,6 +107,7 @@ type eval struct { inliningControl *inliningControl runtime *ast.Term builtinErrors *builtinErrors + stackCapture *stackTraceCapture roundTripper CustomizeRoundTripper evaluated *EvaluatedRuleTracker genvarprefix string @@ -134,8 +135,8 @@ var ( deecPool = util.NewSyncPool[deferredEarlyExitContainer]() resolverPool = util.NewSyncPool[evalResolver]() arraysRecPool = util.NewSyncPool[biunifyArraysRecParams]() - evalFuncPool = util.NewResettablePool[evalFunc, *evalFunc]() - evalBuiltinPool = util.NewResettablePool[evalBuiltin, *evalBuiltin]() + evalFuncPool = util.NewResettablePool[evalFunc]() + evalBuiltinPool = util.NewResettablePool[evalBuiltin]() ) func (e *eval) Run(iter evalIterator) error { @@ -377,17 +378,13 @@ func (e *eval) evalExpr(iter evalIterator) error { } if e.cancel != nil && e.cancel.Cancelled() { + cancelErr := &Error{Code: CancelErr, Message: "caller cancelled query execution"} if e.ctx != nil && e.ctx.Err() != nil { - return &Error{ - Code: CancelErr, - Message: e.ctx.Err().Error(), - err: e.ctx.Err(), - } - } - return &Error{ - Code: CancelErr, - Message: "caller cancelled query execution", + err := e.ctx.Err() + cancelErr.Message = err.Error() + cancelErr.err = err } + return cancelErr } if e.index >= len(e.query) { @@ -415,9 +412,13 @@ func (e *eval) evalExpr(iter evalIterator) error { return e.evalWith(iter) } - return e.evalStep(func(e *eval) error { + err := e.evalStep(func(e *eval) error { return e.next(iter) }) + + // The innermost point an error passes through with every enclosing query's + // expression index still intact. + return e.withStackTrace(err) } func (e *eval) evalStep(iter evalIterator) (err error) { @@ -736,20 +737,20 @@ func (e *eval) evalWith(iter evalIterator) error { input, err := mergeTermWithValues(e.input, pairsInput) if err != nil { - return &Error{ + return e.withStackTrace(&Error{ Code: ConflictErr, Location: expr.Location, Message: err.Error(), - } + }) } data, err := mergeTermWithValues(e.data, pairsData) if err != nil { - return &Error{ + return e.withStackTrace(&Error{ Code: ConflictErr, Location: expr.Location, Message: err.Error(), - } + }) } oldInput, oldData, pushedFrame := e.evalWithPush(input, data, functionMocks, targets, disable) @@ -760,6 +761,7 @@ func (e *eval) evalWith(iter evalIterator) error { oldInput, oldData, pushedFrame = e.evalWithPush(input, data, functionMocks, targets, disable) return err }) + err = e.withStackTrace(err) e.evalWithPop(oldInput, oldData, pushedFrame) @@ -925,7 +927,6 @@ func (e *eval) evalNotPartial(expr *ast.Expr, unNegateFn unNegateFn, complementF } func (e *eval) evalNotPartialSupport(negationID uint64, expr *ast.Expr, supportTermsFn supportTermsFn, unknowns ast.VarSet, queries []ast.Body, iter evalIterator) error { - // Prepare support rule head. supportName := fmt.Sprintf("__not%d_%d_%d__", e.queryID, e.index, negationID) term := ast.RefTerm(ast.DefaultRootDocument, e.saveNamespace, ast.StringTerm(supportName)) @@ -938,27 +939,15 @@ func (e *eval) evalNotPartialSupport(negationID uint64, expr *ast.Expr, supportT bodyVars.Update(q.Vars(ast.VarVisitorParams{})) } - unknowns = unknowns.Intersect(bodyVars) - // Make rule args. Sort them to ensure order is deterministic. - args := make([]*ast.Term, 0, len(unknowns)) - - for v := range unknowns { - args = append(args, ast.NewTerm(v)) - } - - slices.SortFunc(args, ast.TermValueCompare) - + args := util.SortedFunc(util.MapKeys(unknowns.Intersect(bodyVars), ast.ToTerm), ast.TermValueCompare) if len(args) > 0 { - head.Args = args + head.Args = util.SortedFunc(args, ast.TermValueCompare) } // Save support rules. for _, query := range queries { - e.saveSupport.Insert(path, &ast.Rule{ - Head: head, - Body: query, - }) + e.saveSupport.Insert(path, &ast.Rule{Head: head, Body: query}) } // Save expression that refers to support rule set. @@ -1016,19 +1005,28 @@ func (e *eval) evalCall(terms []*ast.Term, iter unifyIterator) error { ir, err = e.getRules(ref, terms[1:], index) } defer ast.IndexResultPool.Put(ir) - if err != nil { + if err != nil || ir == nil { return err } - if ir == nil { - return nil - } - eval := evalFuncPool.Get() - defer evalFuncPool.Put(eval) + // Since values may outlive the function in partial evaluation, + // only use pooled evalFunc when not doing partial eval. + var eval *evalFunc + if e.partial() { + eval = &evalFunc{} + } else { + eval = evalFuncPool.Get() + defer evalFuncPool.Put(eval) + } eval.e = e - eval.terms = terms eval.ir = ir + eval.terms = slices.Grow(eval.terms, len(terms))[:len(terms)] + copy(eval.terms, terms) + + if eval.cacheKey == nil { + eval.cacheKey, eval.args = make(ast.Ref, 0, 8), make([]*ast.Term, 0, 8) + } return eval.eval(iter) } @@ -1306,8 +1304,7 @@ func (e *eval) biunifyValues(a, b *ast.Term, b1, b2 *bindings, iter unifyIterato // Sets must not contain unbound variables at this point as we cannot unify // them. So simply plug both sides (to substitute any bound variables with // values) and then check for equality. - switch a.Value.(type) { - case ast.Set: + if _, ok := a.Value.(ast.Set); ok { a = b1.Plug(a) b = b2.Plug(b) } @@ -1369,7 +1366,6 @@ func (e *eval) biunifyRef(a, b *ast.Term, b1, b2 *bindings, iter unifyIterator) } func (e *eval) biunifyComprehension(a, b *ast.Term, b1, b2 *bindings, swap bool, iter unifyIterator) error { - if e.unknown(a, b1) { return e.biunifyComprehensionPartial(a, b, b1, b2, swap, iter) } @@ -1427,13 +1423,7 @@ func (e *eval) buildComprehensionCache(a *ast.Term) (*ast.Term, error) { e.instr.counterIncr(evalOpComprehensionCacheHit) } - values := make([]*ast.Term, len(index.Keys)) - - for i := range index.Keys { - values[i] = e.bindings.Plug(index.Keys[i]) - } - - return cache.Get(values), nil + return cache.Get(util.Map(index.Keys, e.bindings.Plug)), nil } func (e *eval) buildComprehensionCacheArray(x *ast.ArrayComprehension, keys []*ast.Term) (*comprehensionCacheElem, error) { @@ -1443,10 +1433,7 @@ func (e *eval) buildComprehensionCacheArray(x *ast.ArrayComprehension, keys []*a e.childWithBindingSizeHint(x.Body, child, ast.EstimateBodyBindingCount(x.Body)) node := newComprehensionCacheElem() return node, child.Run(func(child *eval) error { - values := make([]*ast.Term, len(keys)) - for i := range keys { - values[i] = child.bindings.Plug(keys[i]) - } + values := util.Map(keys, child.bindings.Plug) head := child.bindings.Plug(x.Term) cached := node.Get(values) if cached != nil { @@ -1465,10 +1452,7 @@ func (e *eval) buildComprehensionCacheSet(x *ast.SetComprehension, keys []*ast.T e.childWithBindingSizeHint(x.Body, child, ast.EstimateBodyBindingCount(x.Body)) node := newComprehensionCacheElem() return node, child.Run(func(child *eval) error { - values := make([]*ast.Term, len(keys)) - for i := range keys { - values[i] = child.bindings.Plug(keys[i]) - } + values := util.Map(keys, child.bindings.Plug) head := child.bindings.Plug(x.Term) cached := node.Get(values) if cached != nil { @@ -1488,10 +1472,7 @@ func (e *eval) buildComprehensionCacheObject(x *ast.ObjectComprehension, keys [] e.childWithBindingSizeHint(x.Body, child, ast.EstimateBodyBindingCount(x.Body)) node := newComprehensionCacheElem() return node, child.Run(func(child *eval) error { - values := make([]*ast.Term, len(keys)) - for i := range keys { - values[i] = child.bindings.Plug(keys[i]) - } + values := util.Map(keys, child.bindings.Plug) headKey := child.bindings.Plug(x.Key) headValue := child.bindings.Plug(x.Value) cached := node.Get(values) @@ -1818,6 +1799,17 @@ type evalResolver struct { args []*ast.Term } +// IndexEveryCandidateEvaluated implements ast.IndexEveryCandidateEvaluated. +// +// Partial evaluation never exits early, whatever its definitions resolve to: +// evalExpr raises the error only where `!e.partial()`. So it evaluates every +// candidate even under a ruleset IndexResult.EarlyExit says a caller could stop in +// -- that field is what the ruleset permits, not what this caller does, which is +// why an index has to ask. +func (e *evalResolver) IndexEveryCandidateEvaluated() bool { + return e.e.partial() +} + func (e *evalResolver) Resolve(ref ast.Ref) (ast.Value, error) { e.e.instr.startTimer(evalOpResolve) @@ -2009,24 +2001,20 @@ func (e *eval) rewrittenVar(v ast.Var) (ast.Var, bool) { } func (e *eval) getDeclArgsLen(x *ast.Expr) (int, error) { - if !x.IsCall() { return -1, nil } operator := x.Operator() bi, _, ok := e.builtinFunc(operator.String()) - if ok { return bi.Decl.Arity(), nil } ir, err := e.getRules(operator, nil, e.ruleIndex(operator)) defer ast.IndexResultPool.Put(ir) - if err != nil { + if err != nil || ir == nil || ir.Empty() { return -1, err - } else if ir == nil || ir.Empty() { - return -1, nil } return len(ir.Rules[0].Head.Args), nil @@ -2076,13 +2064,7 @@ func operandRequiresEval(v ast.Value) bool { } func (e *evalBuiltin) eval(iter unifyIterator) error { - - operands := make([]*ast.Term, len(e.terms)) - - for i := range e.terms { - operands[i] = e.e.bindings.Plug(e.terms[i]) - } - + operands := util.Map(e.terms, e.e.bindings.Plug) numDeclArgs := e.bi.Decl.Arity() // NOTE(philipc): We sometimes have to drop the very last term off @@ -2147,12 +2129,9 @@ func (e *evalBuiltin) eval(iter unifyIterator) error { } // Normal unification flow for builtins: - err := e.f(bctx, operands, func(output *ast.Term) error { - + err := e.f(bctx, operands, func(output *ast.Term) (err error) { e.e.instr.stopTimer(evalOpBuiltinCall) - var err error - switch { case e.bi.Decl.Result() == nil: err = iter() @@ -2186,6 +2165,13 @@ func (e *evalBuiltin) eval(iter unifyIterator) error { if t, ok := err.(Halt); ok { err = t.Err } else { + // Built-in errors are collected here rather than unwinding through + // evalExpr, so the stack has to be recorded now. Skip it when the + // collected errors have no consumer: query.go drops them, and a + // policy over messy data reaches this for every row. + if c := e.e.stackCapture; c != nil && c.builtinErrors { + err = e.e.attachStackTrace(err) + } e.e.builtinErrors.errs = append(e.e.builtinErrors.errs, err) err = nil } @@ -2196,15 +2182,21 @@ func (e *evalBuiltin) eval(iter unifyIterator) error { } type evalFunc struct { - e *eval - ir *ast.IndexResult - terms []*ast.Term + terms []*ast.Term + cacheKey []*ast.Term + args []*ast.Term + e *eval + ir *ast.IndexResult } // Reset clears the fields before this evalFunc is returned to its pool, // so pooling it doesn't keep terms/index results from the previous call alive. func (e *evalFunc) Reset() { - e.e, e.terms, e.ir = nil, nil, nil + clear(e.terms) + clear(e.cacheKey) + clear(e.args) + e.terms, e.cacheKey, e.args = e.terms[:0], e.cacheKey[:0], e.args[:0] + e.e, e.ir = nil, nil } func (e *evalFunc) eval(iter unifyIterator) error { @@ -2264,35 +2256,28 @@ func (e *evalFunc) eval(iter unifyIterator) error { } func (e *evalFunc) evalValue(iter unifyIterator, argCount int, findOne bool) error { - var cacheKey ast.Ref if !e.e.partial() { - var hit bool - var err error - cacheKey, hit, err = e.evalCache(argCount, iter) - if err != nil { + hit, err := e.evalCache(argCount, iter) + if err != nil || hit { return err - } else if hit { - return nil } } - // NOTE(anders): While it makes the code a bit more complex, reusing the - // args slice across each function increment saves a lot of resources - // compared to creating a new one inside each call to evalOneRule... so - // think twice before simplifying this :) - args := make([]*ast.Term, len(e.terms)-1) + numArgs := len(e.terms) - 1 + e.args = slices.Grow(e.args, numArgs)[:numArgs] var prev *ast.Term return withSuppressEarlyExit(func() error { var outerEe *deferredEarlyExitError for _, rule := range e.ir.Rules { - copy(args, rule.Head.Args) - if len(args) == len(rule.Head.Args)+1 { - args[len(args)-1] = rule.Head.Value + copy(e.args, rule.Head.Args) + numHeadArgs := len(rule.Head.Args) + if numArgs == numHeadArgs+1 { + e.args[numArgs-1] = rule.Head.Value } - next, err := e.evalOneRule(iter, rule, args, cacheKey, prev, findOne) + next, err := e.evalOneRule(iter, rule, prev, findOne) if err != nil { if oee, ok := err.(*deferredEarlyExitError); ok { if outerEe == nil { @@ -2304,12 +2289,12 @@ func (e *evalFunc) evalValue(iter unifyIterator, argCount int, findOne bool) err } if next == nil { for _, erule := range e.ir.Else[rule] { - copy(args, erule.Head.Args) - if len(args) == len(erule.Head.Args)+1 { - args[len(args)-1] = erule.Head.Value + copy(e.args, erule.Head.Args) + if numArgs == numHeadArgs+1 { + e.args[numArgs-1] = erule.Head.Value } - next, err = e.evalOneRule(iter, erule, args, cacheKey, prev, findOne) + next, err = e.evalOneRule(iter, erule, prev, findOne) if err != nil { if oee, ok := err.(*deferredEarlyExitError); ok { if outerEe == nil { @@ -2330,12 +2315,12 @@ func (e *evalFunc) evalValue(iter unifyIterator, argCount int, findOne bool) err } if e.ir.Default != nil && prev == nil { - copy(args, e.ir.Default.Head.Args) - if len(args) == len(e.ir.Default.Head.Args)+1 { - args[len(args)-1] = e.ir.Default.Head.Value + copy(e.args, e.ir.Default.Head.Args) + if numArgs == len(e.ir.Default.Head.Args)+1 { + e.args[numArgs-1] = e.ir.Default.Head.Value } - _, err := e.evalOneRule(iter, e.ir.Default, args, cacheKey, prev, findOne) + _, err := e.evalOneRule(iter, e.ir.Default, prev, findOne) return err } @@ -2348,46 +2333,43 @@ func (e *evalFunc) evalValue(iter unifyIterator, argCount int, findOne bool) err }) } -func (e *evalFunc) evalCache(argCount int, iter unifyIterator) (ast.Ref, bool, error) { +func (e *evalFunc) evalCache(argCount int, iter unifyIterator) (bool, error) { plen := len(e.terms) if plen == argCount+2 { // func name + output = 2 plen -= 1 } - cacheKey := make([]*ast.Term, plen) + e.cacheKey = slices.Grow(e.cacheKey, plen)[:plen] for i := range plen { - if e.terms[i].IsGround() { - // Avoid expensive copying of ref if it is ground. - cacheKey[i] = e.terms[i] - } else { - cacheKey[i] = e.e.bindings.Plug(e.terms[i]) + e.cacheKey[i] = e.terms[i] // Avoid expensive copying of ref if ground + if !e.terms[i].IsGround() { + e.cacheKey[i] = e.e.bindings.Plug(e.terms[i]) } } - cached, _ := e.e.virtualCache.Get(cacheKey) - if cached != nil { + if cached, _ := e.e.virtualCache.Get(e.cacheKey); cached != nil { e.e.instr.counterIncr(evalOpVirtualCacheHit) if argCount == len(e.terms)-1 { // f(x) if ast.Boolean(false).Equal(cached.Value) { - return nil, true, nil + return true, nil } - return nil, true, iter() + return true, iter() } // f(x, y), y captured output value - return nil, true, e.e.unify(e.terms[len(e.terms)-1] /* y */, cached, iter) + return true, e.e.unify(e.terms[len(e.terms)-1] /* y */, cached, iter) } e.e.instr.counterIncr(evalOpVirtualCacheMiss) - return cacheKey, false, nil + return false, nil } -func (e *evalFunc) evalOneRule(iter unifyIterator, rule *ast.Rule, args []*ast.Term, cacheKey ast.Ref, prev *ast.Term, findOne bool) (*ast.Term, error) { +func (e *evalFunc) evalOneRule(iter unifyIterator, rule *ast.Rule, prev *ast.Term, findOne bool) (*ast.Term, error) { child := evalPool.Get() defer evalPool.Put(child) // Optimization: pre-size bindings based on function argument count to reduce memory waste. // Function argument count is known at compile time and most functions have < 10 arguments. // This avoids allocating the default 16-slot array when only 2-3 bindings are needed. - sizeHint := len(args) + sizeHint := len(e.args) e.e.childWithBindingSizeHint(rule.Body, child, sizeHint) child.findOne = findOne @@ -2395,25 +2377,26 @@ func (e *evalFunc) evalOneRule(iter unifyIterator, rule *ast.Rule, args []*ast.T child.traceEnter(rule) - err := child.biunifyTerms(e.terms[1:], args, e.e.bindings, child.bindings, func() error { + err := child.biunifyTerms(e.terms[1:], e.args, e.e.bindings, child.bindings, func() error { return child.eval(func(child *eval) error { child.traceExit(rule) e.e.evaluated.Record(rule) // Partial evaluation must save an expression that tests the output value if the output value // was not captured to handle the case where the output value may be `false`. - if len(rule.Head.Args) == len(e.terms)-1 && e.e.saveSet.Contains(rule.Head.Value, child.bindings) { + noOutputCapture := len(rule.Head.Args) == len(e.terms)-1 + if noOutputCapture && e.e.saveSet.Contains(rule.Head.Value, child.bindings) { err := e.e.saveExpr(ast.NewExpr(rule.Head.Value), child.bindings, iter) child.traceRedo(rule) return err } result = child.bindings.Plug(rule.Head.Value) - if cacheKey != nil { - e.e.virtualCache.Put(cacheKey, result) // the redos confirm this, or the evaluation is aborted + if e.cacheKey != nil { + e.e.virtualCache.Put(e.cacheKey, result) // the redos confirm this, or the evaluation is aborted } - if len(rule.Head.Args) == len(e.terms)-1 && ast.Boolean(false).Equal(result.Value) { + if noOutputCapture && ast.Boolean(false).Equal(result.Value) { if prev != nil && !prev.Equal(result) { return functionConflictErr(rule.Location) } @@ -2452,15 +2435,13 @@ func (e *evalFunc) partialEvalSupport(declArgsLen int, iter unifyIterator) error if !e.e.saveSupport.Exists(path) { for _, rule := range e.ir.Rules { - err := e.partialEvalSupportRule(rule, path) - if err != nil { + if err := e.partialEvalSupportRule(rule, path); err != nil { return err } } if e.ir.Default != nil { - err := e.partialEvalSupportRule(e.ir.Default, path) - if err != nil { + if err := e.partialEvalSupportRule(e.ir.Default, path); err != nil { return err } } @@ -2470,9 +2451,11 @@ func (e *evalFunc) partialEvalSupport(declArgsLen int, iter unifyIterator) error return nil } - term := ast.NewTerm(path) + terms := make([]*ast.Term, len(e.terms)) + terms[0] = ast.NewTerm(path) + copy(terms[1:], e.terms[1:]) - return e.e.saveCall(declArgsLen, append([]*ast.Term{term}, e.terms[1:]...), iter) + return e.e.saveCall(declArgsLen, terms, iter) } func (e *evalFunc) partialEvalSupportRule(rule *ast.Rule, path ast.Ref) error { @@ -2501,6 +2484,7 @@ func (e *evalFunc) partialEvalSupportRule(rule *ast.Rule, path ast.Ref) error { // Skip this rule body if it fails to type-check. // Type-checking failure means the rule body will never succeed. if e.e.compiler.PassesTypeCheck(plugged) { + e.e.evaluated.Record(rule) head := &ast.Head{ Name: rule.Head.Name, Reference: rule.Head.Reference, @@ -2532,12 +2516,12 @@ type deferredEarlyExitContainer struct { } func (dc *deferredEarlyExitContainer) handleErr(err error) error { - if err == nil { - return nil - } - - if dc.deferred == nil && errors.As(err, &dc.deferred) && dc.deferred != nil { - return nil + if err != nil && dc.deferred == nil { + var ok bool + dc.deferred, ok = errors.AsType[*deferredEarlyExitError](err) + if ok && dc.deferred != nil { + return nil + } } return err @@ -2581,7 +2565,6 @@ func (e evalTree) eval(iter unifyIterator) error { } func (e evalTree) finish(iter unifyIterator) error { - // In some cases, it may not be possible to PE the ref. If the path refers // to virtual docs that PE does not support or base documents where inlining // has been disabled, then we have to save. @@ -2598,7 +2581,6 @@ func (e evalTree) finish(iter unifyIterator) error { } func (e evalTree) next(iter unifyIterator, plugged *ast.Term) error { - var node *ast.TreeNode cpy := e @@ -2651,19 +2633,14 @@ func (e evalTree) next(iter unifyIterator, plugged *ast.Term) error { cacheRef = append(cacheRef, k) } - if !expand { + if !expand && e.e.partial() { // The parameter key(s) are not ground, so we cannot // select a concrete sub-source. Under partial evaluation // the reference is unknown and must be residualized; // otherwise it is simply undefined and we fall through // with the bare (rule-less) external node. - if e.e.partial() { - saved := make(ast.Ref, len(e.ref)) - for i := range e.ref { - saved[i] = e.bindings.Plug(e.ref[i]) - } - return e.e.saveUnify(ast.NewTerm(saved), e.rterm, e.bindings, e.rbindings, iter) - } + saved := ast.Ref(util.Map(e.ref, e.bindings.Plug)) + return e.e.saveUnify(ast.NewTerm(saved), e.rterm, e.bindings, e.rbindings, iter) } } @@ -2787,6 +2764,7 @@ func (e evalTree) enumerate(iter unifyIterator) error { // Use method value to avoid closure allocation. // Create once and reuse for both doc and virtual doc enumeration. en := enumerateNext{iter: iter, e: &e, key: nil} + call := en.call if doc != nil { switch doc := doc.(type) { @@ -2794,7 +2772,7 @@ func (e evalTree) enumerate(iter unifyIterator) error { for i := range doc.Len() { k := ast.InternedTerm(i) en.key = k - err := e.e.biunify(k, e.ref[e.pos], e.bindings, e.bindings, en.call) + err := e.e.biunify(k, e.ref[e.pos], e.bindings, e.bindings, call) if err := dc.handleErr(err); err != nil { return err @@ -2804,7 +2782,7 @@ func (e evalTree) enumerate(iter unifyIterator) error { ki := doc.KeysIterator() for k, more := ki.Next(); more; k, more = ki.Next() { en.key = k - err := e.e.biunify(k, e.ref[e.pos], e.bindings, e.bindings, en.call) + err := e.e.biunify(k, e.ref[e.pos], e.bindings, e.bindings, call) if err := dc.handleErr(err); err != nil { return err } @@ -2813,7 +2791,7 @@ func (e evalTree) enumerate(iter unifyIterator) error { // Use Slice() to avoid closure allocation in Iter() for _, elem := range doc.Slice() { en.key = elem - err := e.e.biunify(elem, e.ref[e.pos], e.bindings, e.bindings, en.call) + err := e.e.biunify(elem, e.ref[e.pos], e.bindings, e.bindings, call) if err := dc.handleErr(err); err != nil { return err } @@ -2840,7 +2818,7 @@ func (e evalTree) enumerate(iter unifyIterator) error { } en.key = key - if err := e.e.biunify(key, e.ref[e.pos], e.bindings, e.bindings, en.call); err != nil { + if err := e.e.biunify(key, e.ref[e.pos], e.bindings, e.bindings, call); err != nil { return err } } @@ -2953,17 +2931,12 @@ type evalVirtual struct { } func (e evalVirtual) eval(iter unifyIterator) error { - ir, err := e.e.getRules(e.plugged[:e.pos+1], nil, e.e.ruleIndex(e.plugged[:e.pos+1])) defer ast.IndexResultPool.Put(ir) - if err != nil { + if err != nil || ir == nil { return err } - if ir == nil { - return nil - } - // Partial evaluation of ordered rules is not supported currently. Save the // expression and continue. This could be revisited in the future. if len(ir.Else) > 0 && e.e.unknownRef(e.ref, e.bindings) { @@ -3051,8 +3024,7 @@ func (h *evalVirtualPartialCacheHint) keyWithoutScope() ast.Ref { } func (e evalVirtualPartial) eval(iter unifyIterator) error { - unknown := e.e.unknown(e.ref[:e.pos+1], e.bindings) - + unknown := e.e.unknownRef(e.ref[:e.pos+1], e.bindings) if len(e.ref) == e.pos+1 { if unknown { return e.partialEvalSupport(iter) @@ -3091,7 +3063,7 @@ func (e evalVirtualPartial) evalEachRule(iter unifyIterator, unknown bool) error if e.e.partial() { m := maxRefLength(e.ir.Rules, len(e.ref)) - if e.e.unknown(e.ref[e.pos+1:m], e.bindings) { + if e.e.unknownRef(e.ref[e.pos+1:m], e.bindings) { for _, rule := range e.ir.Rules { if err := e.evalOneRulePostUnify(iter, rule); err != nil { return err @@ -3395,6 +3367,7 @@ func (e evalVirtualPartial) partialEvalSupportRule(rule *ast.Rule, _ ast.Ref) (b // Skip this rule body if it fails to type-check. // Type-checking failure means the rule body will never succeed. if e.e.compiler.PassesTypeCheck(plugged) { + e.e.evaluated.Record(rule) var value *ast.Term if rule.Head.Value != nil { @@ -3458,10 +3431,9 @@ func (e evalVirtualPartial) evalTerm(iter unifyIterator, pos int, term *ast.Term } func (e evalVirtualPartial) evalCache(iter unifyIterator) (evalVirtualPartialCacheHint, error) { - var hint evalVirtualPartialCacheHint - if e.e.unknown(e.ref[:e.pos+1], e.bindings) { + if e.e.unknownRef(e.ref[:e.pos+1], e.bindings) { // FIXME: Return empty hint if unknowns in any e.ref elem overlapping with applicable rule refs? return hint, nil } @@ -3534,8 +3506,7 @@ func (e evalVirtualPartial) evalCache(iter unifyIterator) (evalVirtualPartialCac scope.Ref = append(scope.Ref, plugged) hint.key[len(hint.key)-1] = ast.NewTerm(scope) } else { - scope = vcKeyScope{} - scope.Ref = append(scope.Ref, plugged) + scope = vcKeyScope{Ref: ast.Ref{plugged}} hint.key = append(hint.key, ast.NewTerm(scope)) } } @@ -3770,7 +3741,6 @@ type evalVirtualComplete struct { } func (e evalVirtualComplete) eval(iter unifyIterator) error { - if e.ir.Empty() { return nil } @@ -3892,9 +3862,8 @@ func (e evalVirtualComplete) evalValueRule(iter unifyIterator, rule *ast.Rule, p e.e.evaluated.Record(rule) result = child.bindings.Plug(rule.Head.Value) - if prev != nil { - if ast.Compare(result, prev) != 0 { + if !prev.Equal(result) { return completeDocConflictErr(rule.Location) } child.traceRedo(rule) @@ -3927,6 +3896,7 @@ func (e evalVirtualComplete) partialEval(iter unifyIterator) error { err := child.eval(func(child *eval) error { child.traceExit(rule) + e.e.evaluated.Record(rule) term, termbindings := child.bindings.apply(rule.Head.Value) if err := e.evalTerm(iter, term, termbindings); err != nil { @@ -4012,6 +3982,7 @@ func (e evalVirtualComplete) partialEvalSupportRule(rule *ast.Rule, packagePath // Skip this rule body if it fails to type-check. // Type-checking failure means the rule body will never succeed. if e.e.compiler.PassesTypeCheck(plugged) { + e.e.evaluated.Record(rule) head := ast.RefHead(ruleRef, child.bindings.PlugNamespaced(rule.Head.Value, e.e.caller.bindings)) if !e.e.inliningControl.shallow { @@ -4081,6 +4052,15 @@ func (e evalTerm) eval(iter unifyIterator) error { func (e evalTerm) next(iter unifyIterator, plugged *ast.Term) error { + // Key selects an unknown sub-document: save the reference instead of reading + // a concrete document that may lack the key, or hold a stand-in value. The + // partial() test is inline to keep the call off the non-partial hot path. + if e.e.partial() { + if ref, ok := e.unknownPath(plugged); ok { + return e.e.saveUnify(ast.NewTerm(ref), e.rterm, e.bindings, e.rbindings, iter) + } + } + term, bindings := e.get(plugged) if term == nil { return nil @@ -4093,6 +4073,80 @@ func (e evalTerm) next(iter unifyIterator, plugged *ast.Term) error { return cpy.eval(iter) } +// pluggedPrefix returns e.ref[:e.pos] with variables resolved. +func (e evalTerm) pluggedPrefix() ast.Ref { + prefix := make(ast.Ref, e.pos) + for i := range prefix { + prefix[i] = e.bindings.Plug(e.ref[i]) + } + return prefix +} + +// unknownPath reports whether descending into key lands on an unknown, and if +// so returns the full plugged reference to save. +func (e evalTerm) unknownPath(key *ast.Term) (ast.Ref, bool) { + ref := make(ast.Ref, len(e.ref)) + for i := range ref { + if i == e.pos { + ref[i] = key + } else { + ref[i] = e.bindings.Plug(e.ref[i]) + } + } + + if !e.e.saveSet.Covers(ref[:e.pos+1]) { + return nil, false + } + return ref, true +} + +// enumerateUnknownKeys branches on each key an unknown contributes below the +// current prefix but the concrete document lacks. Without it, input[k] with +// input.x unknown and input = {"y": 2} would only ever consider k = "y". +func (e evalTerm) enumerateUnknownKeys(iter unifyIterator, handleErr func(error) error) error { + prefix := e.pluggedPrefix() + + for _, k := range e.e.saveSet.Keys(prefix) { + if term, _ := e.get(k); term != nil { + continue // already covered by the concrete enumeration above + } + // Nothing concrete to descend into: save the whole remaining reference. + ref := make(ast.Ref, len(e.ref)) + copy(ref, prefix) + ref[e.pos] = k + for i := e.pos + 1; i < len(ref); i++ { + ref[i] = e.bindings.Plug(e.ref[i]) + } + // Positions below the key can still rule the branch out: with input.x.a + // unknown, input[k].b has nothing to match at k = "x". + if !e.e.saveSet.ContainsOverlapping(ast.NewTerm(ref), e.bindings) { + continue + } + err := e.e.biunify(k, e.ref[e.pos], e.bindings, e.bindings, func() error { + return e.e.saveUnify(ast.NewTerm(ref), e.rterm, e.bindings, e.rbindings, iter) + }) + if err := handleErr(err); err != nil { + return err + } + } + + return nil +} + +// evalTermNext is the evalTerm counterpart of enumerateNext: it lets the +// object/set enumeration loops pass a method value to biunify instead of a +// function literal, which would escape to the heap on every iteration. +// evalTerm is held by value so call() doesn't chase a second pointer. +type evalTermNext struct { + e evalTerm + iter unifyIterator + key *ast.Term +} + +func (en *evalTermNext) call() error { + return en.e.next(en.iter, en.e.termbindings.Plug(en.key)) +} + func (e evalTerm) enumerate(iter unifyIterator) error { var deferredEe *deferredEarlyExitError handleErr := func(err error) error { @@ -4137,10 +4191,14 @@ func (e evalTerm) enumerate(iter unifyIterator) error { } } case ast.Object: - for _, k := range v.Keys() { - err := e.e.biunify(k, e.ref[e.pos], e.termbindings, e.bindings, func() error { - return e.next(iter, e.termbindings.Plug(k)) - }) + // Bind the method value once, outside the loop: a func literal — or a method + // value materialized per iteration — escapes to the heap on every key. + en := evalTermNext{iter: iter, e: e} + call := en.call + ki := v.KeysIterator() + for k, more := ki.Next(); more; k, more = ki.Next() { + en.key = k + err := e.e.biunify(k, e.ref[e.pos], e.termbindings, e.bindings, call) if err != nil { if err := handleErr(err); err != nil { return err @@ -4148,10 +4206,11 @@ func (e evalTerm) enumerate(iter unifyIterator) error { } } case ast.Set: + en := evalTermNext{iter: iter, e: e} + call := en.call for _, elem := range v.Slice() { - err := e.e.biunify(elem, e.ref[e.pos], e.termbindings, e.bindings, func() error { - return e.next(iter, e.termbindings.Plug(elem)) - }) + en.key = elem + err := e.e.biunify(elem, e.ref[e.pos], e.termbindings, e.bindings, call) if err != nil { if err := handleErr(err); err != nil { return err @@ -4160,6 +4219,12 @@ func (e evalTerm) enumerate(iter unifyIterator) error { } } + if e.e.partial() { + if err := e.enumerateUnknownKeys(iter, handleErr); err != nil { + return err + } + } + if deferredEe != nil { return deferredEe } @@ -4436,7 +4501,7 @@ func (e evalNot) evalPartial(iter evalIterator) error { expr := e.e.query[e.e.index] - unNegate := func(expr *ast.Expr) ast.Body { + unNegate := func(*ast.Expr) ast.Body { return e.not.Body } @@ -4725,7 +4790,6 @@ func plugKeys(a ast.Object, b *bindings) ast.Object { } func canInlineNegation(safe ast.VarSet, queries []ast.Body) bool { - size := 1 vis := newNestedCheckVisitor() @@ -4793,7 +4857,6 @@ func (v *nestedCheckVisitor) visit(x any) bool { } func containsNestedRefOrCall(vis *nestedCheckVisitor, expr *ast.Expr) bool { - if expr.IsEquality() { for _, term := range expr.Operands() { if containsNestedRefOrCallInTerm(vis, term) { @@ -4849,10 +4912,7 @@ func containsNestedRefOrCallInTerm(vis *nestedCheckVisitor, term *ast.Term) bool return false default: vis.vis.Walk(v) - if vis.found { - return true - } - return false + return vis.found } } @@ -4931,7 +4991,7 @@ func merge(a, b ast.Value) (ast.Value, bool) { // objects can be merged with other objects. If the values cannot be merged, // objB value will be overwritten by objA value. func mergeObjects(objA, objB ast.Object) (result ast.Object, ok bool) { - result = ast.NewObject() + result = ast.NewObjectWithCapacity(objA.Len() + objB.Len()) stop := objA.Until(func(k, v *ast.Term) bool { if v2 := objB.Get(k); v2 == nil { result.Insert(k, v) diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/glob.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/glob.go index 4e80c519ba..8924710401 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/glob.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/glob.go @@ -16,7 +16,7 @@ const globInterQueryValueCacheHits = "rego_builtin_glob_interquery_value_cache_h var noDelimiters = []rune{} var dotDelimiters = []rune{'.'} var globCacheLock = sync.RWMutex{} -var globCache = map[string]glob.Glob{} +var globCache = map[string]*glob.Pattern{} func builtinGlobMatch(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { pattern, err := builtins.StringOperand(operands[0].Value, 1) @@ -66,7 +66,7 @@ func globCompileAndMatch(bctx BuiltinContext, id, pattern, match string, delimit // TODO: Use named cache val, ok := bctx.InterQueryBuiltinValueCache.Get(ast.String(id)) if ok { - pat, valid := val.(glob.Glob) + pat, valid := val.(*glob.Pattern) if !valid { // The cache key may exist for a different value type (eg. regex). // In this case, we calculate the glob and return the result w/o updating the cache. diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/http.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/http.go index 62fd92c351..839725497b 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/http.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/http.go @@ -1333,13 +1333,13 @@ func newHTTPRequestExecutor(bctx BuiltinContext, req ast.Object, key ast.Object) } if useInterQueryCache && bctx.InterQueryBuiltinCache != nil { - return newInterQueryCache(bctx, req, key, forceCacheParams) + return newInterQueryCache(bctx, req, key, forceCacheParams), nil } - return newIntraQueryCache(bctx, req, key) + return newIntraQueryCache(bctx, req, key), nil } -func newInterQueryCache(bctx BuiltinContext, req ast.Object, key ast.Object, forceCacheParams forceCacheParams) (*interQueryCache, error) { - return &interQueryCache{bctx: bctx, req: req, key: key, forceCacheParams: forceCacheParams}, nil +func newInterQueryCache(bctx BuiltinContext, req ast.Object, key ast.Object, forceCacheParams forceCacheParams) *interQueryCache { + return &interQueryCache{bctx: bctx, req: req, key: key, forceCacheParams: forceCacheParams} } // CheckCache checks the cache for the value of the key set on this object @@ -1401,8 +1401,8 @@ func (c *interQueryCache) ExecuteHTTPRequest() (*http.Response, error) { return executeHTTPRequest(c.httpReq, c.httpClient, c.req) } -func newIntraQueryCache(bctx BuiltinContext, req ast.Object, key ast.Object) (*intraQueryCache, error) { - return &intraQueryCache{bctx: bctx, req: req, key: key}, nil +func newIntraQueryCache(bctx BuiltinContext, req ast.Object, key ast.Object) *intraQueryCache { + return &intraQueryCache{bctx: bctx, req: req, key: key} } // CheckCache checks the cache for the value of the key set on this object diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/json.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/json.go index bb8cc4925f..53989541b3 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/json.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/json.go @@ -11,6 +11,7 @@ import ( "github.com/open-policy-agent/opa/v1/ast" "github.com/open-policy-agent/opa/v1/topdown/builtins" + "github.com/open-policy-agent/opa/v1/util" "github.com/open-policy-agent/opa/internal/edittree" ) @@ -150,22 +151,15 @@ func getJSONPaths(operand ast.Value) (paths []ast.Ref, err error) { paths = append(paths, filter) } case ast.Set: - paths = make([]ast.Ref, 0, v.Len()) - for _, item := range v.Slice() { - filter, err := parsePath(item) - if err != nil { - return nil, err - } - paths = append(paths, filter) - } + paths, err = util.TryMap(v.Slice(), parsePath) default: return nil, builtins.NewOperandTypeErr(2, v, "set", "array") } - return paths, nil + return paths, err } -// parsePath parses a JSON pointer path or array of path segments into an ast.Ref. +// parsePath parsese a JSON pointer path or array of path segments into an ast.Ref. func parsePath(path *ast.Term) (ast.Ref, error) { // paths can either be a `/` separated json path or // an array or set of values diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/query.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/query.go index e912bbae59..399b7f0a5b 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/query.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/query.go @@ -1,6 +1,7 @@ package topdown import ( + "cmp" "context" "crypto/rand" "io" @@ -57,6 +58,7 @@ type Query struct { interQueryBuiltinValueCache cache.InterQueryValueCache ndBuiltinCache builtins.NDBCache strictBuiltinErrors bool + stackTraces bool builtinErrorList *[]Error strictObjects bool roundTripper CustomizeRoundTripper @@ -272,6 +274,21 @@ func (q *Query) WithStrictBuiltinErrors(yes bool) *Query { return q } +// WithStackTraces tells the evaluator to record the stack of queries being +// evaluated when an error occurred on the returned *Error. The stack is exposed +// as Error.StackTrace and left out of the error message, so callers render it +// themselves. +// +// Off by default because capture is not free: each *Error costs a walk of the +// parent chain and a frame per query on it, resolved against the bindings in +// scope, which on a query collecting one built-in error per row runs from +60% +// to +203% in time (see BenchmarkStackTraceCollectedBuiltinErrors). OPA's own +// CLI and server accept that cost and turn it on. +func (q *Query) WithStackTraces(yes bool) *Query { + q.stackTraces = yes + return q +} + // WithBuiltinErrorList supplies a pointer to an Error slice to store built-in function errors // encountered during evaluation. This error slice can be inspected after evaluation to determine // which built-in function errors occurred. @@ -365,9 +382,7 @@ func (q *Query) WithEvaluatedRuleTracker(t *EvaluatedRuleTracker) *Query { // evaluation may produce additional support modules that should be used in // conjunction with the partially evaluated queries. func (q *Query) PartialRun(ctx context.Context) (partials []ast.Body, support []*ast.Module, err error) { - if q.partialNamespace == "" { - q.partialNamespace = "partial" // lazily initialize partial namespace - } + q.partialNamespace = cmp.Or(q.partialNamespace, "partial") if q.evaluated != nil && q.compiler != nil { q.evaluated.WithAnnotationSet(q.compiler.GetAnnotationSet()) } @@ -380,21 +395,7 @@ func (q *Query) PartialRun(ctx context.Context) (partials []ast.Body, support [] q.metrics = util.Or(q.metrics, metrics.New) f := &queryIDFactory{} - b := newBindings(0, q.instr) - - var vc VirtualCache - if q.virtualCache != nil { - vc = q.virtualCache - } else { - vc = NewVirtualCache() - } - - var bc BaseCache - if q.baseCache != nil { - bc = q.baseCache - } else { - bc = newBaseCache() - } + b := newBindings(q.instr) e := &eval{ ctx: ctx, @@ -409,12 +410,13 @@ func (q *Query) PartialRun(ctx context.Context) (partials []ast.Body, support [] bindings: b, compiler: q.compiler, store: q.store, - baseCache: bc, + baseCache: util.Or(q.baseCache, newBaseCache), txn: q.txn, input: q.input, external: q.external, tracers: q.tracers, traceEnabled: len(q.tracers) > 0, + stackCapture: q.newStackCapture(), plugTraceVars: q.plugTraceVars, instr: q.instr, builtins: q.builtins, @@ -422,7 +424,7 @@ func (q *Query) PartialRun(ctx context.Context) (partials []ast.Body, support [] interQueryBuiltinCache: q.interQueryBuiltinCache, interQueryBuiltinValueCache: q.interQueryBuiltinValueCache, ndBuiltinCache: q.ndBuiltinCache, - virtualCache: vc, + virtualCache: util.Or(q.virtualCache, NewVirtualCache), saveSet: newSaveSet(q.unknowns, b, q.instr), saveStack: newSaveStack(), saveSupport: newSaveSupport(), @@ -462,38 +464,37 @@ func (q *Query) PartialRun(ctx context.Context) (partials []ast.Body, support [] } } - ast.WalkVars(q.query, func(x ast.Var) bool { - if !x.IsGenerated() { - livevars.Add(x) - } - return false - }) + // iterate expressions to avoid Body -> any boxing in WalkVars argument + for _, expr := range q.query { + ast.WalkVars(expr, func(x ast.Var) bool { + if !x.IsGenerated() { + livevars.Add(x) + } + return false + }) + } p := copypropagation.New(livevars).WithCompiler(q.compiler) err = e.Run(func(e *eval) error { - // Build output from saved expressions. - body := ast.NewBody() - - for _, elem := range e.saveStack.Peek() { - body.Append(elem.Plug(e.bindings)) + saved := e.saveStack.Peek() + exprs := make([]*ast.Expr, 0, len(saved)+e.bindings.size()) + for _, elem := range saved { + exprs = append(exprs, elem.Plug(e.bindings)) } // Include bindings as exprs so that when caller evals the result, they // can obtain values for the vars in their query. - bindingExprs := []*ast.Expr{} _ = e.bindings.Iter(e.bindings, func(a, b *ast.Term) error { - bindingExprs = append(bindingExprs, ast.Equality.Expr(a, b)) + exprs = append(exprs, ast.Equality.Expr(a, b)) return nil }) // cannot return error // Sort binding expressions so that results are deterministic. - slices.SortFunc(bindingExprs, (*ast.Expr).Compare) + slices.SortFunc(exprs[len(saved):], (*ast.Expr).Compare) - for i := range bindingExprs { - body.Append(bindingExprs[i]) - } + body := ast.NewBody(exprs...) // Skip this rule body if it fails to type-check. // Type-checking failure means the rule body will never succeed. @@ -509,8 +510,6 @@ func (q *Query) PartialRun(ctx context.Context) (partials []ast.Body, support [] return nil }) - support = e.saveSupport.List() - if len(e.builtinErrors.errs) > 0 { if q.strictBuiltinErrors { err = e.builtinErrors.errs[0] @@ -532,6 +531,8 @@ func (q *Query) PartialRun(ctx context.Context) (partials []ast.Body, support [] } } + support = e.saveSupport.List() + for i, m := range support { if regoVersion := q.compiler.DefaultRegoVersion(); regoVersion != ast.RegoUndefined { ast.SetModuleRegoVersion(m, q.compiler.DefaultRegoVersion()) @@ -557,16 +558,11 @@ func (q *Query) Run(ctx context.Context) (QueryResultSet, error) { func (q *Query) Iter(ctx context.Context, iter func(QueryResult) error) error { // Query evaluation must not be allowed if the compiler has errors and is in an undefined, possibly inconsistent state if q.compiler != nil && len(q.compiler.Errors) > 0 { - return &Error{ - Code: InternalErr, - Message: "compiler has errors", - } + return &Error{Code: InternalErr, Message: "compiler has errors"} } - if q.evaluated != nil && q.compiler != nil { q.evaluated.WithAnnotationSet(q.compiler.GetAnnotationSet()) } - if q.seed == nil { q.seed = rand.Reader } @@ -576,21 +572,6 @@ func (q *Query) Iter(ctx context.Context, iter func(QueryResult) error) error { q.metrics = util.Or(q.metrics, metrics.New) f := &queryIDFactory{} - - var vc VirtualCache - if q.virtualCache != nil { - vc = q.virtualCache - } else { - vc = NewVirtualCache() - } - - var bc BaseCache - if q.baseCache != nil { - bc = q.baseCache - } else { - bc = newBaseCache() - } - e := &eval{ ctx: ctx, metrics: q.metrics, @@ -601,15 +582,16 @@ func (q *Query) Iter(ctx context.Context, iter func(QueryResult) error) error { queryCompiler: q.queryCompiler, queryIDFact: f, queryID: f.Next(), - bindings: newBindings(0, q.instr), + bindings: newBindings(q.instr), compiler: q.compiler, store: q.store, - baseCache: bc, + baseCache: util.Or(q.baseCache, newBaseCache), txn: q.txn, input: q.input, external: q.external, tracers: q.tracers, traceEnabled: len(q.tracers) > 0, + stackCapture: q.newStackCapture(), plugTraceVars: q.plugTraceVars, instr: q.instr, builtins: q.builtins, @@ -617,7 +599,7 @@ func (q *Query) Iter(ctx context.Context, iter func(QueryResult) error) error { interQueryBuiltinCache: q.interQueryBuiltinCache, interQueryBuiltinValueCache: q.interQueryBuiltinValueCache, ndBuiltinCache: q.ndBuiltinCache, - virtualCache: vc, + virtualCache: util.Or(q.virtualCache, NewVirtualCache), genvarprefix: q.genvarprefix, runtime: q.runtime, indexing: q.indexing, @@ -637,7 +619,7 @@ func (q *Query) Iter(ctx context.Context, iter func(QueryResult) error) error { e.caller = e q.metrics.Timer(metrics.RegoQueryEval).Start() err := e.Run(func(e *eval) error { - qr := QueryResult{} + qr := make(QueryResult, e.bindings.size()) _ = e.bindings.Iter(nil, func(k, v *ast.Term) error { qr[k.Value.(ast.Var)] = v return nil diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/regex.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/regex.go index 0313452033..6c6de304dc 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/regex.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/regex.go @@ -26,6 +26,41 @@ var ( regexpCache = make(map[string]*regexp.Regexp) ) +func regexpCacheGet(pat string) (*regexp.Regexp, error) { + regexpCacheLock.RLock() + v, ok := regexpCache[pat] + regexpCacheLock.RUnlock() + if ok { + return v, nil + } + + // cache miss! + re, err := regexp.Compile(pat) + if err != nil { + return nil, err + } + + regexpCacheLock.Lock() + + // Ensure the cache is below the max size. + for len(regexpCache) >= regexCacheMaxSize { + // Since every caller inserts at most one item, we expect this + // loop to run for at most one iteration. + for k := range regexpCache { + // Go map iteration is semi-random, so this deletes a + // more or less arbitrary key. + delete(regexpCache, k) + break + } + } + + regexpCache[pat] = re + + regexpCacheLock.Unlock() + return re, nil + +} + func builtinRegexIsValid(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { if s, err := builtins.StringOperand(operands[0].Value, 1); err == nil { if _, err = syntax.Parse(string(s), syntax.Perl); err == nil { @@ -129,46 +164,16 @@ func getRegexp(bctx BuiltinContext, pat string) (*regexp.Regexp, error) { return re, nil } - regexpCacheLock.RLock() - re, ok := regexpCache[pat] - numCached := len(regexpCache) - regexpCacheLock.RUnlock() - if !ok { - var err error - re, err = regexp.Compile(pat) - if err != nil { - return nil, err - } - - regexpCacheLock.Lock() - if numCached >= regexCacheMaxSize { - // Delete a (semi-)random key to make room for the new one. - for k := range regexpCache { - delete(regexpCache, k) - break - } - } - regexpCache[pat] = re - regexpCacheLock.Unlock() - } - return re, nil + return regexpCacheGet(pat) } func getRegexpTemplate(pat string, delimStart, delimEnd byte) (*regexp.Regexp, error) { - regexpCacheLock.RLock() - re, ok := regexpCache[pat] - regexpCacheLock.RUnlock() - if !ok { - var err error - re, err = compileRegexTemplate(pat, delimStart, delimEnd) - if err != nil { - return nil, err - } - regexpCacheLock.Lock() - regexpCache[pat] = re - regexpCacheLock.Unlock() + gen, err := generateRegexTemplate(pat, delimStart, delimEnd) + if err != nil { + return nil, err } - return re, nil + + return regexpCacheGet(gen) } func builtinGlobsMatch(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/regex_template.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/regex_template.go index 0c1f698bfe..f8cc550876 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/regex_template.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/regex_template.go @@ -67,19 +67,13 @@ func delimiterIndices(s string, delimiterStart, delimiterEnd byte) ([]int, error return idxs, nil } -// compileRegexTemplate parses a template and returns a Regexp. -// -// You can define your own delimiters. It is e.g. common to use curly braces {} but I recommend using characters -// which have no special meaning in Regex, e.g.: <, > -// -// reg, err := compiler.CompileRegex("foo:bar.baz:<[0-9]{2,10}>", '<', '>') -// // if err != nil ... -// reg.MatchString("foo:bar.baz:123") -func compileRegexTemplate(tpl string, delimiterStart, delimiterEnd byte) (*regexp.Regexp, error) { +// generateRegexTemplate creates and returns the pattern string compiled by +// compileRegexTemplate(). +func generateRegexTemplate(tpl string, delimiterStart, delimiterEnd byte) (string, error) { // Check if it is well-formed. idxs, errBraces := delimiterIndices(tpl, delimiterStart, delimiterEnd) if errBraces != nil { - return nil, errBraces + return "", errBraces } varsR := make([]*regexp.Regexp, len(idxs)/2) pattern := bytes.NewBufferString("^") @@ -96,7 +90,7 @@ func compileRegexTemplate(tpl string, delimiterStart, delimiterEnd byte) (*regex fmt.Fprintf(pattern, "%s(%s)", regexp.QuoteMeta(raw), patt) varsR[varIdx], err = regexp.Compile(fmt.Sprintf("^%s$", patt)) if err != nil { - return nil, err + return "", err } } @@ -109,6 +103,23 @@ func compileRegexTemplate(tpl string, delimiterStart, delimiterEnd byte) (*regex // WriteByte's error value is always nil for bytes.Buffer, no need to check it. pattern.WriteByte('$') + return pattern.String(), nil +} + +// compileRegexTemplate parses a template and returns a Regexp. +// +// You can define your own delimiters. It is e.g. common to use curly braces {} but I recommend using characters +// which have no special meaning in Regex, e.g.: <, > +// +// reg, err := compiler.CompileRegex("foo:bar.baz:<[0-9]{2,10}>", '<', '>') +// // if err != nil ... +// reg.MatchString("foo:bar.baz:123") +func compileRegexTemplate(tpl string, delimiterStart, delimiterEnd byte) (*regexp.Regexp, error) { + pattern, err := generateRegexTemplate(tpl, delimiterStart, delimiterEnd) + if err != nil { + return nil, err + } + // Compile full regexp. - return regexp.Compile(pattern.String()) + return regexp.Compile(pattern) } diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/save.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/save.go index 30d250f9c6..5d23623392 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/save.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/save.go @@ -91,6 +91,76 @@ func (ss *saveSet) containsrec(t *ast.Term, b *bindings) bool { return found } +// ContainsOverlapping is a conservative Contains: it also reports terms that +// may refer to an unknown once resolved, like input[k] when input.x is unknown. +// Callers saving whole expressions need it; evalTerm saves per branch instead. +func (ss *saveSet) ContainsOverlapping(t *ast.Term, b *bindings) bool { + if ss == nil { + return false + } + ss.instr.startTimer(partialOpSaveSetContains) + defer ss.instr.stopTimer(partialOpSaveSetContains) + + other, ok := t.Value.(ast.Ref) + if !ok { + return ss.contains(t, b) + } + + for el := ss.l.Back(); el != nil; el = el.Prev() { + elem := el.Value.(*saveSetElem) + for _, ref := range elem.refs { + if refsMayOverlap(ref, other) { + return true + } + } + if elem.containsVar(other[0], b) { + return true + } + } + return false +} + +// Covers reports whether an unknown sits at or above path, i.e. the whole +// sub-document is unknown. Directional half of Contains: input.z.a being +// unknown leaves input.z.b known, so a walk through input.z keeps descending. +func (ss *saveSet) Covers(path ast.Ref) bool { + if ss == nil { + return false + } + for el := ss.l.Back(); el != nil; el = el.Prev() { + if slices.ContainsFunc(el.Value.(*saveSetElem).refs, path.HasPrefix) { + return true + } + } + return false +} + +// Keys returns the ground keys unknowns contribute directly below prefix. With +// input.x unknown and input = {"y": 2}, iterating input[k] must still produce a +// branch for k = "x". A non-ground prefix matches no unknown, hence no keys. +func (ss *saveSet) Keys(prefix ast.Ref) []*ast.Term { + if ss == nil { + return nil + } + + var keys []*ast.Term + for el := ss.l.Back(); el != nil; el = el.Prev() { + for _, ref := range el.Value.(*saveSetElem).refs { + if len(ref) <= len(prefix) || !ref.HasPrefix(prefix) { + continue + } + k := ref[len(prefix)] + if !k.IsGround() { + continue + } + if !slices.ContainsFunc(keys, k.Equal) { + keys = append(keys, k) + } + } + } + return keys +} + func (ss *saveSet) Vars(caller *bindings) ast.VarSet { result := ast.NewVarSet() for x := ss.l.Front(); x != nil; x = x.Next() { @@ -158,6 +228,31 @@ func (sse *saveSetElem) Contains(t *ast.Term, b *bindings) bool { return false } +// refsMayOverlap returns true if ref (an unknown) and other could refer to the +// same document. Non-ground positions act as wildcards: input[k] may hit +// input.x. Heads are exempt -- ref[0] names a root doc, not an unbound position. +func refsMayOverlap(ref, other ast.Ref) bool { + if len(ref) == 0 || len(other) == 0 { + return true + } + + if !ref[0].Equal(other[0]) { + return false + } + + for i, n := 1, min(len(ref), len(other)); i < n; i++ { + // Two ground positions that differ are the only thing ruling an overlap + // out; a non-ground position on either side acts as a wildcard. `other` + // is the side carrying variables, so test its groundness first. + x, y := ref[i], other[i] + if !x.Equal(y) && y.IsGround() && x.IsGround() { + return false + } + } + + return true +} + func (sse *saveSetElem) String() string { return fmt.Sprintf("(refs: %v, vars: %v, b: %v)", sse.refs, sse.vars, sse.b) } @@ -275,11 +370,7 @@ func newSaveSupport() *saveSupport { } func (s *saveSupport) List() []*ast.Module { - result := make([]*ast.Module, 0, len(s.modules)) - for _, module := range s.modules { - result = append(result, module) - } - return result + return util.Values(s.modules) } func (s *saveSupport) Exists(path ast.Ref) bool { @@ -378,7 +469,7 @@ func saveRequired(compilerTree *ast.TreeNode, extStack *externalTreeStack, ic *i found = true } case ast.Ref: - if ss.Contains(node, b) { + if ss.ContainsOverlapping(node, b) { found = true } else if ic.Disabled(v.ConstantPrefix(), icIgnoreInternal) { found = true diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/stacktrace.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/stacktrace.go new file mode 100644 index 0000000000..cf019a9cfc --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/stacktrace.go @@ -0,0 +1,336 @@ +// Copyright 2026 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "bytes" + "slices" + "strconv" + "strings" + + "github.com/open-policy-agent/opa/v1/ast" +) + +// maxStackFrameTextLength keeps frames with large literals in them readable. +const maxStackFrameTextLength = 80 + +// StackFrame is one query in a StackTrace. +type StackFrame struct { + // QueryID matches the QueryID of the trace events for the same query. + QueryID uint64 `json:"query_id"` + + // Location is the expression being evaluated, nil if the query has no + // location information. + Location *ast.Location `json:"location,omitempty"` + + // text is the source at Location with the values bound to its variables + // spliced in, empty when nothing was bound. Unexported to keep the policy + // source out of the marshaled frame; String reports it. + text string +} + +// String returns the frame's position and, when the query has source, the +// expression at it with the values its variables were bound to spliced in. +func (f StackFrame) String() string { + s := strings.Builder{} + f.writeTo(&s) + return s.String() +} + +func (f StackFrame) writeTo(s *strings.Builder) { + loc := f.Location + if loc == nil { + s.WriteString("") + return + } + + if loc.File != "" { + s.WriteString(loc.File) + s.WriteByte(':') + s.WriteString(strconv.Itoa(loc.Row)) + } else { + s.WriteString(strconv.Itoa(loc.Row)) + s.WriteByte(':') + s.WriteString(strconv.Itoa(loc.Col)) + } + + src := f.text + if src == "" && loc.Text != nil { + src = string(loc.Text) + } + + if text := frameText(src); text != "" { + s.WriteString(": ") + s.WriteString(text) + } +} + +// StackTrace is the stack of queries being evaluated when an error occurred, +// innermost first. +type StackTrace []StackFrame + +// String returns one indented frame per line. +func (st StackTrace) String() string { + s := strings.Builder{} + for i := range st { + if i > 0 { + s.WriteByte('\n') + } + s.WriteString(" ") + st[i].writeTo(&s) + } + return s.String() +} + +// frameText collapses src onto one line and truncates it. +func frameText(src string) string { + text := strings.Join(strings.Fields(src), " ") + + var runes int + for i := range text { + if runes == maxStackFrameTextLength { + return text[:i] + "..." + } + runes++ + } + + return text +} + +// stackTrace captures the evaluation stack, innermost first. Enclosing queries +// are still suspended on the call stack here, so their expression indices have +// not been unwound yet. +// +// The chain is walked twice to size the slice: append would cost an allocation +// per doubling, on every error a deep stack raises. +func (e *eval) stackTrace() StackTrace { + var depth int + for curr := e; curr != nil; curr = curr.parent { + depth++ + } + + st := make(StackTrace, 0, depth) + for curr := e; curr != nil; curr = curr.parent { + expr := curr.currentExpr() + if expr == nil { + st = append(st, StackFrame{QueryID: curr.queryID}) + continue + } + st = append(st, StackFrame{ + QueryID: curr.queryID, + Location: expr.Location, + text: curr.resolvedText(expr), + }) + } + return st +} + +// currentExpr returns the expression e is evaluating. Once the whole body has +// succeeded e.index sits one past the end, leaving nothing to point at, so the +// last expression is reported as the nearest position. +func (e *eval) currentExpr() *ast.Expr { + if len(e.query) == 0 { + return nil + } + return e.query[min(e.index, len(e.query)-1)] +} + +// resolvedText renders the source of expr with the values its variables are +// bound to spliced in, so a frame reads fn(1) where the policy wrote fn(x) - the +// argument a call failed on is usually the reason it failed. Returns "" when +// nothing was substituted and the source stands on its own. +// +// Bindings are unwound as evaluation backtracks, so this has to run while the +// error is being raised rather than when the frame is rendered. +func (e *eval) resolvedText(expr *ast.Expr) string { + loc := expr.Location + if loc == nil || len(loc.Text) == 0 { + return "" + } + + capture := e.stackCapture + capture.vars = appendReadVars(capture.vars[:0], expr) + subs := capture.subs[:0] + + for _, t := range capture.vars { + start, ok := sourceSpan(loc, t.Location) + if !ok { + continue + } + + // An unbound var plugs to itself, and a value too long to fit in a frame + // is better left as the name the policy gave it. + bound := e.bindings.Plug(t) + if bound == t || !bound.IsGround() || bound.StringLength() > maxStackFrameTextLength { + continue + } + + subs = append(subs, textSub{start: start, end: start + len(t.Location.Text), value: bound.String()}) + } + + capture.subs = subs + + if len(subs) == 0 { + return "" + } + + slices.SortFunc(subs, func(a, b textSub) int { return a.start - b.start }) + + s := strings.Builder{} + prev := 0 + for _, sub := range subs { + // A var reached twice, or one nested in the span of another, resolves to + // the same text the first one already wrote. + if sub.start < prev { + continue + } + s.Write(loc.Text[prev:sub.start]) + s.WriteString(sub.value) + prev = sub.end + } + s.Write(loc.Text[prev:]) + + return s.String() +} + +// textSub is a span of an expression's source to replace with a value. +type textSub struct { + start, end int + value string +} + +// stackTraceCapture is the state Query.WithStackTraces turns on. A non-nil one +// on an eval means capture is enabled, so the feature adds a single field to a +// struct that is copied for every query. +type stackTraceCapture struct { + // builtinErrors records whether collected built-in errors have a consumer. + // Without one query.go drops them, and a policy over messy data reaches that + // path for every row. + builtinErrors bool + + // Working space for resolvedText, shared by every eval of the query - + // evaluation is single threaded - rather than reallocated per stack. + vars []*ast.Term + subs []textSub +} + +// newStackCapture returns the capture state to share across q's evals, nil when +// stack traces are off and nothing will ask for it. +func (q *Query) newStackCapture() *stackTraceCapture { + if !q.stackTraces { + return nil + } + return &stackTraceCapture{builtinErrors: q.strictBuiltinErrors || q.builtinErrorList != nil} +} + +// appendReadVars appends every variable expr reads to dst. ast.WalkTerms would +// find the same ones, but it allocates a visitor and a closure per call and this +// runs once per frame of every captured stack. +// +// Positions that declare a variable rather than read one are skipped, so an +// every keeps its own name in the head instead of reading `every 1 in [1]`. So +// are comprehension bodies: their variables belong to a child query's bindings, +// which the frame cannot resolve. +func appendReadVars(dst []*ast.Term, expr *ast.Expr) []*ast.Term { + switch terms := expr.Terms.(type) { + case *ast.Term: + dst = appendVarsInTerm(dst, terms) + case []*ast.Term: + // terms[0] is the operator, a ref to a built-in or to a rule. + for _, t := range terms[1:] { + dst = appendVarsInTerm(dst, t) + } + case *ast.Every: + // Key and Value are the every's to declare, but its body reads them. + dst = appendVarsInTerm(dst, terms.Domain) + for _, e := range terms.Body { + dst = appendReadVars(dst, e) + } + } + + for _, w := range expr.With { + dst = appendVarsInTerm(dst, w.Value) + } + + return dst +} + +func appendVarsInTerm(dst []*ast.Term, t *ast.Term) []*ast.Term { + // Composites track their groundness, so this prunes most of the walk for + // the cost of a field read. + if t.IsGround() { + return dst + } + + switch v := t.Value.(type) { + case ast.Var: + dst = append(dst, t) + case ast.Ref: + for _, t := range v { + dst = appendVarsInTerm(dst, t) + } + case ast.Call: + for _, t := range v[1:] { + dst = appendVarsInTerm(dst, t) + } + case *ast.Array: + for i := range v.Len() { + dst = appendVarsInTerm(dst, v.Elem(i)) + } + case ast.Set: + // Slice and Keys allocate where Foreach wouldn't, but a closure over dst + // would put it on the heap for every call, ground terms included. + for _, t := range v.Slice() { + dst = appendVarsInTerm(dst, t) + } + case ast.Object: + for _, k := range v.Keys() { + dst = appendVarsInTerm(dst, k) + dst = appendVarsInTerm(dst, v.Get(k)) + } + } + + return dst +} + +// sourceSpan returns where term's source sits within expr's, and false when the +// two don't line up. Terms shared across a policy - the data root document, for +// one - carry the location of wherever they were first parsed, so matching the +// offset alone isn't enough. +func sourceSpan(expr, term *ast.Location) (int, bool) { + if term == nil || len(term.Text) == 0 || term.File != expr.File { + return 0, false + } + + start := term.Offset - expr.Offset + end := start + len(term.Text) + if start < 0 || end > len(expr.Text) || !bytes.Equal(expr.Text[start:end], term.Text) { + return 0, false + } + + return start, true +} + +// withStackTrace records the evaluation stack on err, if enabled. Kept small +// enough to inline, so the disabled case costs only a branch. +func (e *eval) withStackTrace(err error) error { + if err == nil || e.stackCapture == nil { + return err + } + return e.attachStackTrace(err) +} + +// attachStackTrace returns err carrying the evaluation stack, or unchanged if it +// already has one - the innermost stack wins. err is copied, not annotated in +// place: shared errors like errInScopeWithStmt would otherwise race, and leak +// one query's stack into every later occurrence. +func (e *eval) attachStackTrace(err error) error { + if tdErr, ok := err.(*Error); ok && tdErr.StackTrace == nil { + cpy := *tdErr + cpy.StackTrace = e.stackTrace() + return &cpy + } + return err +} diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/strings.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/strings.go index 69495cc8bb..e26ec2c39c 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/strings.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/strings.go @@ -71,10 +71,7 @@ func builtinAnySuffixMatch(_ BuiltinContext, operands []*ast.Term, iter func(*as if err != nil { return err } - strsReversed = make([]string, len(strs)) - for i := range strs { - strsReversed[i] = reverseString(strs[i]) - } + strsReversed = util.Map(strs, reverseString) default: return builtins.NewOperandTypeErr(1, a, "string", "set", "array") } @@ -88,10 +85,7 @@ func builtinAnySuffixMatch(_ BuiltinContext, operands []*ast.Term, iter func(*as if err != nil { return err } - suffixesReversed = make([]string, len(suffixes)) - for i := range suffixes { - suffixesReversed[i] = reverseString(suffixes[i]) - } + suffixesReversed = util.Map(suffixes, reverseString) default: return builtins.NewOperandTypeErr(2, b, "string", "set", "array") } @@ -791,7 +785,7 @@ func builtinSprintf(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) // Optimized path for where sprintf is used as a "to_string" function for // a single integer, i.e. sprintf("%d", [x]) where x is an integer. if s == "%d" && a.Len() == 1 { - if n, ok := a.Elem(0).Value.(ast.Number); ok { + if n, ok := a.Elem(0).Value.(ast.Number); ok && !isFloatNumber(string(n)) { if i, ok := n.Int(); ok { if interned := ast.InternedIntegerString(i); interned != nil { return iter(interned) @@ -808,23 +802,18 @@ func builtinSprintf(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) switch v := t.Value.(type) { case ast.Number: ns := string(v) - if x, ok := util.Atoi64(ns); ok { - args[i] = x - } else { - if strings.ContainsRune(ns, '.') { - if f, ok := v.Float64(); ok { - args[i] = f - continue - } else { - args[i] = ns - } + if isFloatNumber(ns) { + if f, ok := v.Float64(); ok { + args[i] = f } else { - if b, ok := new(big.Int).SetString(ns, 10); ok { - args[i] = b - } else { - args[i] = ns - } + args[i] = ns } + } else if x, ok := util.Atoi64(ns); ok { + args[i] = x + } else if b, ok := new(big.Int).SetString(ns, 10); ok { + args[i] = b + } else { + args[i] = ns } case ast.String: args[i] = string(v) @@ -836,6 +825,15 @@ func builtinSprintf(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) return iter(ast.InternedTerm(fmt.Sprintf(string(s), args...))) } +// isFloatNumber reports whether the textual representation of a number is that +// of a floating point value, i.e. it has a fraction or an exponent. Since +// util.Atoi64 parses numbers with only zeros past the decimal point (1.0) as +// integers, the text, and not the parsed value, decides how a number is +// formatted by sprintf. +func isFloatNumber(s string) bool { + return strings.ContainsAny(s, ".eE") +} + func builtinReverse(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { s, err := builtins.StringOperand(operands[0].Value, 1) if err != nil { diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/subset.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/subset.go index d50dc2db77..93fd75e13e 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/subset.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/subset.go @@ -182,7 +182,7 @@ func arraySubset(super, sub *ast.Array) bool { } subElem := sub.Elem(subCursor) - if superElem.Value.Compare(subElem.Value) == 0 { + if superElem.Equal(subElem) { subCursor++ } else { superCursor++ diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/template_string.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/template_string.go index 0e705b81bf..c9a4984a76 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/template_string.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/template_string.go @@ -20,7 +20,7 @@ func builtinTemplateString(bctx BuiltinContext, operands []*ast.Term, iter func( buf := make([]string, arr.Len()) var count int - err = builtinPrintCrossProductOperands(bctx.Location, buf, arr, 0, func(buf []string) error { + err = builtinPrintCrossProductOperands(bctx.Location, buf, arr, 0, func([]string) error { count += 1 // Precautionary run-time assertion that template-strings can't produce multiple outputs; e.g. for custom relation type built-ins not known at compile-time. if count > 1 { diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/tokens.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/tokens.go index 622f376c77..ffd3d5e008 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/tokens.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/tokens.go @@ -822,9 +822,7 @@ func tokenHeaderString(name string, where *string, value ast.Value) error { // parseTokenHeader parses the JWT header. func parseTokenHeader(token *JSONWebToken) (*tokenHeader, error) { - header := tokenHeader{ - unknown: []string{}, - } + header := tokenHeader{} if err := token.decodedHeader.Iter(func(k *ast.Term, v *ast.Term) error { ks := string(k.Value.(ast.String)) handler, ok := tokenHeaderTypes[ks] diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/trace.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/trace.go index ae6191f5e2..ceb8f7e108 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/trace.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/trace.go @@ -20,7 +20,7 @@ import ( ) const ( - minLocationWidth = 5 // len("query") + minLocationWidth = len("query") maxIdealLocationWidth = 64 columnPadding = 4 maxExprVarWidth = 32 @@ -267,10 +267,6 @@ type PrettyTraceOptions struct { type traceRow []string -func (r *traceRow) add(s string) { - *r = append(*r, s) -} - type traceTable struct { rows []traceRow maxWidths []int @@ -315,10 +311,10 @@ func PrettyTraceWithOpts(w io.Writer, trace []*Event, opts PrettyTraceOptions) { row := traceRow{} if opts.Locations { - row.add(formatLocation(event, filePathAliases)) + row = append(row, formatLocation(event, filePathAliases)) } - row.add(formatEvent(event, depth)) + row = append(row, formatEvent(event, depth)) if opts.ExprVariables { vars := exprLocalVars(event) @@ -342,7 +338,7 @@ func PrettyTraceWithOpts(w io.Writer, trace []*Event, opts PrettyTraceOptions) { } buf.WriteByte('}') - row.add(buf.String()) + row = append(row, buf.String()) } if opts.LocalVariables { @@ -365,9 +361,9 @@ func PrettyTraceWithOpts(w io.Writer, trace []*Event, opts PrettyTraceOptions) { } buf.WriteByte('}') - row.add(buf.String()) + row = append(row, buf.String()) } else { - row.add("{}") + row = append(row, "{}") } } @@ -652,7 +648,7 @@ func (v varInfo) Value() string { func (v varInfo) Title() string { if v.exprLoc != nil && v.exprLoc.Text != nil { - return string(v.exprLoc.Text) + return util.ByteSliceToString(v.exprLoc.Text) } return string(v.Name) } @@ -662,8 +658,7 @@ func padLocationText(loc *ast.Location) string { return "" } - text := string(loc.Text) - + text := util.ByteSliceToString(loc.Text) if loc.Col == 0 { return text } @@ -868,11 +863,7 @@ func printPrettyVars(w *bytes.Buffer, exprVars map[string]varInfo) { return } - byCol := make([]varInfo, 0, len(exprVars)) - for _, info := range exprVars { - byCol = append(byCol, info) - } - slices.SortFunc(byCol, func(a, b varInfo) int { + byCol := util.SortedFunc(util.Values(exprVars), func(a, b varInfo) int { // sort first by column, then by reverse row (to present vars in the same order they appear in the expr) if a.col == b.col { if a.exprLoc.Row == b.exprLoc.Row { diff --git a/vendor/github.com/open-policy-agent/opa/v1/types/decode.go b/vendor/github.com/open-policy-agent/opa/v1/types/decode.go index f2515d5df1..a571d565f4 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/types/decode.go +++ b/vendor/github.com/open-policy-agent/opa/v1/types/decode.go @@ -25,8 +25,7 @@ const ( ) // Unmarshal deserializes bs and returns the resulting type. -func Unmarshal(bs []byte) (result Type, err error) { - +func Unmarshal[T byte, BS ~[]byte](bs BS) (result Type, err error) { var hint rawtype if err = util.UnmarshalJSON(bs, &hint); err == nil { @@ -45,7 +44,7 @@ func Unmarshal(bs []byte) (result Type, err error) { var err error var static []Type var dynamic Type - if static, err = unmarshalSlice(arr.Static); err != nil { + if static, err = util.TryMap(arr.Static, Unmarshal); err != nil { return nil, err } if len(arr.Dynamic) != 0 { @@ -81,14 +80,14 @@ func Unmarshal(bs []byte) (result Type, err error) { var union rawunion if err = util.UnmarshalJSON(bs, &union); err == nil { var of []Type - if of, err = unmarshalSlice(union.Of); err == nil { + if of, err = util.TryMap(union.Of, Unmarshal); err == nil { result = NewAny(of...) } } case typeFunction: var decl rawdecl if err = util.UnmarshalJSON(bs, &decl); err == nil { - args, err := unmarshalSlice(decl.Args) + args, err := util.TryMap(decl.Args, Unmarshal) if err != nil { return nil, err } @@ -155,16 +154,6 @@ type rawdecl struct { Variadic json.RawMessage `json:"variadic"` } -func unmarshalSlice(elems []json.RawMessage) (result []Type, err error) { - result = make([]Type, len(elems)) - for i := range elems { - if result[i], err = Unmarshal(elems[i]); err != nil { - return nil, err - } - } - return result, err -} - func unmarshalStaticPropertySlice(elems []rawstaticproperty) (result []*StaticProperty, err error) { result = make([]*StaticProperty, len(elems)) for i := range elems { diff --git a/vendor/github.com/open-policy-agent/opa/v1/types/types.go b/vendor/github.com/open-policy-agent/opa/v1/types/types.go index c02fee4d40..c9f958161a 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/types/types.go +++ b/vendor/github.com/open-policy-agent/opa/v1/types/types.go @@ -26,10 +26,15 @@ var ( // N represents an instance of the number type. N Type = NewNumber() // A represents the superset of all types. - A Type = NewAny() + A Type = Any{} // Boxed set types. SetOfAny, SetOfStr, SetOfNum Type = NewSet(A), NewSet(S), NewSet(N) + + jsonString = [...]byte{'{', '"', 't', 'y', 'p', 'e', '"', ':', '"', 's', 't', 'r', 'i', 'n', 'g', '"', '}'} + jsonBoolean = [...]byte{'{', '"', 't', 'y', 'p', 'e', '"', ':', '"', 'b', 'o', 'o', 'l', 'e', 'a', 'n', '"', '}'} + jsonNumber = [...]byte{'{', '"', 't', 'y', 'p', 'e', '"', ':', '"', 'n', 'u', 'm', 'b', 'e', 'r', '"', '}'} + jsonNull = [...]byte{'{', '"', 't', 'y', 'p', 'e', '"', ':', '"', 'n', 'u', 'l', 'l', '"', '}'} ) // Sprint returns the string representation of the type. @@ -108,10 +113,8 @@ func Named(name string, t Type) *NamedType { } // MarshalJSON returns the JSON encoding of t. -func (t Null) MarshalJSON() ([]byte, error) { - return json.Marshal(map[string]any{ - "type": t.typeMarker(), - }) +func (Null) MarshalJSON() ([]byte, error) { + return jsonNull[:], nil } func unwrap(t Type) Type { @@ -144,7 +147,7 @@ func NewBoolean() Boolean { // MarshalJSON returns the JSON encoding of t. func (Boolean) MarshalJSON() ([]byte, error) { - return util.StringToByteSlice(`{"type":"boolean"}`), nil + return jsonBoolean[:], nil } func (t Boolean) String() string { @@ -161,7 +164,7 @@ func NewString() String { // MarshalJSON returns the JSON encoding of t. func (String) MarshalJSON() ([]byte, error) { - return util.StringToByteSlice(`{"type":"string"}`), nil + return jsonString[:], nil } func (String) String() string { @@ -178,7 +181,7 @@ func NewNumber() Number { // MarshalJSON returns the JSON encoding of t. func (Number) MarshalJSON() ([]byte, error) { - return util.StringToByteSlice(`{"type":"number"}`), nil + return jsonNumber[:], nil } func (Number) String() string { @@ -219,10 +222,7 @@ func (t *Array) toMap() map[string]any { func (t *Array) String() string { prefix := "array" - buf := make([]string, 0, len(t.static)) - for _, tpe := range t.static { - buf = append(buf, Sprint(tpe)) - } + buf := util.Map(t.static, Sprint) repr := prefix if len(buf) > 0 { repr += "<" + strings.Join(buf, ", ") + ">" @@ -288,6 +288,9 @@ func (t *Set) toMap() map[string]any { } func (t *Set) String() string { + if t.of == nil { + return typeSet + } return typeSet + "[" + Sprint(t.of) + "]" } @@ -647,15 +650,11 @@ func (t Any) Union(other Any) Any { } func (t Any) String() string { - prefix := "any" if len(t) == 0 { - return prefix - } - buf := make([]string, len(t)) - for i := range t { - buf[i] = Sprint(t[i]) + return "any" } - return prefix + "<" + strings.Join(buf, ", ") + ">" + buf := util.Map(t, Sprint) + return "any<" + strings.Join(buf, ", ") + ">" } // Function represents a function type. @@ -720,20 +719,14 @@ func (t *Function) FuncArgs() FuncArgs { // NamedFuncArgs returns the function's arguments, with a name and // description if available. func (t *Function) NamedFuncArgs() FuncArgs { - args := make([]Type, len(t.args)) - copy(args, t.args) - return FuncArgs{Args: args, Variadic: t.variadic} + return FuncArgs{Args: slices.Clone(t.args), Variadic: t.variadic} } // Args returns the function's arguments as a slice, ignoring variadic arguments. // // Deprecated: Use FuncArgs instead. func (t *Function) Args() []Type { - cpy := make([]Type, len(t.args)) - for i := range t.args { - cpy[i] = unwrap(t.args[i]) - } - return cpy + return util.Map(t.args, unwrap) } // Arity returns the number of arguments in the function signature. @@ -856,8 +849,7 @@ func (a FuncArgs) Arg(x int) Type { // Compare returns -1, 0, 1 based on comparison between a and b. func Compare(a, b Type) int { a, b = unwrapRecursive(unwrap(a)), unwrapRecursive(unwrap(b)) - x := typeOrder(a) - y := typeOrder(b) + x, y := typeOrder(a), typeOrder(b) if x > y { return 1 } else if x < y { diff --git a/vendor/github.com/open-policy-agent/opa/v1/util/channel.go b/vendor/github.com/open-policy-agent/opa/v1/util/channel.go deleted file mode 100644 index e2653ac7fd..0000000000 --- a/vendor/github.com/open-policy-agent/opa/v1/util/channel.go +++ /dev/null @@ -1,32 +0,0 @@ -package util - -import ( - "github.com/open-policy-agent/opa/v1/metrics" -) - -// This prevents getting blocked forever writing to a full buffer, in case another routine fills the last space. -// Retrying maxEventRetry times to drop the oldest event. Dropping the incoming event if there still isn't room. -const maxEventRetry = 1000 - -// PushFIFO pushes data into a buffered channel without blocking when full, making room by dropping the oldest data. -// An optional metric can be recorded when data is dropped. -func PushFIFO[T any](buffer chan T, data T, metrics metrics.Metrics, metricName string) { - - for range maxEventRetry { - // non-blocking send to the buffer, to prevent blocking if buffer is full so room can be made. - select { - case buffer <- data: - return - default: - } - - // non-blocking drop from the buffer to make room for incoming event - select { - case <-buffer: - if metrics != nil && metricName != "" { - metrics.Counter(metricName).Incr() - } - default: - } - } -} diff --git a/vendor/github.com/open-policy-agent/opa/v1/util/compare.go b/vendor/github.com/open-policy-agent/opa/v1/util/compare.go index d9e6702d85..3825ef24b3 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/util/compare.go +++ b/vendor/github.com/open-policy-agent/opa/v1/util/compare.go @@ -59,6 +59,14 @@ func SliceLenCompare[T any, S ~[]T](a, b S) int { return 1 } +// CmpEqual is a functional helper for equals comparison of comparable values +// (i.e. using ==), meant to be used for stdlib funtions like [slices.DeleteFunc]. +func CmpEqual[T comparable](a T) func(b T) bool { + return func(b T) bool { + return a == b + } +} + // Compare returns 0 if a equals b, -1 if a is less than b, and 1 if b is than a. // // For comparison between values of different types, the following ordering is used: @@ -77,8 +85,7 @@ func Compare(a, b any) int { case nil: return 0 case bool: - switch b := b.(type) { - case bool: + if b, ok := b.(bool); ok { if a == b { return 0 } @@ -88,13 +95,11 @@ func Compare(a, b any) int { return 1 } case json.Number: - switch b := b.(type) { - case json.Number: + if b, ok := b.(json.Number); ok { return compareJSONNumber(a, b) } case int: - switch b := b.(type) { - case int: + if b, ok := b.(int); ok { if a == b { return 0 } else if a < b { @@ -103,8 +108,7 @@ func Compare(a, b any) int { return 1 } case float64: - switch b := b.(type) { - case float64: + if b, ok := b.(float64); ok { if a == b { return 0 } else if a < b { @@ -113,8 +117,7 @@ func Compare(a, b any) int { return 1 } case string: - switch b := b.(type) { - case string: + if b, ok := b.(string); ok { if a == b { return 0 } else if a < b { @@ -123,8 +126,7 @@ func Compare(a, b any) int { return 1 } case []any: - switch b := b.(type) { - case []any: + if b, ok := b.([]any); ok { bLen := len(b) aLen := len(a) minLen := min(bLen, aLen) @@ -142,8 +144,7 @@ func Compare(a, b any) int { return 1 } case map[string]any: - switch b := b.(type) { - case map[string]any: + if b, ok := b.(map[string]any); ok { aKeys := KeysSorted(a) bKeys := KeysSorted(b) aLen := len(aKeys) diff --git a/vendor/github.com/open-policy-agent/opa/v1/util/errors.go b/vendor/github.com/open-policy-agent/opa/v1/util/errors.go new file mode 100644 index 0000000000..18558129ff --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/v1/util/errors.go @@ -0,0 +1,11 @@ +package util + +import "errors" + +// ErrorIs is a shorthand for [errors.AsType] returning only the boolean result. +// This is typically cheaper than [errors.Is], but it's not a drop-in replacement +// for scenario where e.g. custom `Is` methods need to be taken into account. +func ErrorIs[E error](err error) bool { + _, ok := errors.AsType[E](err) + return ok +} diff --git a/vendor/github.com/open-policy-agent/opa/v1/util/json.go b/vendor/github.com/open-policy-agent/opa/v1/util/json.go index de95ed50bf..cc0e5793f4 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/util/json.go +++ b/vendor/github.com/open-policy-agent/opa/v1/util/json.go @@ -9,11 +9,12 @@ import ( "encoding/json" "fmt" "io" + "maps" "reflect" + "slices" "strconv" - "sigs.k8s.io/yaml" - + "github.com/open-policy-agent/opa/internal/yaml" "github.com/open-policy-agent/opa/v1/loader/extension" ) @@ -140,7 +141,16 @@ func RoundTrip(x *any) error { if err != nil { return err } - return UnmarshalJSON(bs, x) + + // Decode into a fresh value instead of reusing *x: if *x holds a non-nil + // pointer, json.Unmarshal decodes into the pointed-to value in place + // rather than replacing it. + var y any + if err := UnmarshalJSON(bs, &y); err != nil { + return err + } + *x = y + return nil } // NeedsRoundTrip returns true if the value won't change as a result of @@ -156,6 +166,90 @@ func NeedsRoundTrip(x any) bool { return true } +// RoundTripFast is equivalent to [RoundTrip], but recurses natively through +// map[string]any and []any instead of going through JSON bytes, falling +// back to [RoundTrip] for any other type. +func RoundTripFast(x *any) error { + if x == nil { + return nil + } + y, err := roundTripFastValue(*x, 0, nil) + if err != nil { + return err + } + *x = y + return nil +} + +// startDetectingCyclesAfter matches encoding/json's own threshold. +const startDetectingCyclesAfter = 1000 + +// depth/seen detect cycles the way encoding/json does: native recursion +// doesn't get that check for free from json.Marshal like RoundTrip's +// fallback path does. +func roundTripFastValue(v any, depth int, seen map[uintptr]struct{}) (any, error) { + switch x := v.(type) { + case nil, bool, string, json.Number: + return x, nil + case map[string]any: + if x == nil { + return nil, nil + } + ptr := uintptr(reflect.ValueOf(x).UnsafePointer()) + if depth >= startDetectingCyclesAfter { + if _, ok := seen[ptr]; ok { + return nil, fmt.Errorf("json: unsupported value: encountered a cycle via %T", x) + } + seen = markSeen(seen, ptr) + } + cpy := maps.Clone(x) + for k, e := range cpy { + c, err := roundTripFastValue(e, depth+1, seen) + if err != nil { + return nil, err + } + cpy[k] = c + } + delete(seen, ptr) + return cpy, nil + case []any: + if x == nil { + return nil, nil + } + ptr := uintptr(reflect.ValueOf(x).UnsafePointer()) + if depth >= startDetectingCyclesAfter { + if _, ok := seen[ptr]; ok { + return nil, fmt.Errorf("json: unsupported value: encountered a cycle via %T", x) + } + seen = markSeen(seen, ptr) + } + cpy := slices.Clone(x) + for i, e := range cpy { + c, err := roundTripFastValue(e, depth+1, seen) + if err != nil { + return nil, err + } + cpy[i] = c + } + delete(seen, ptr) + return cpy, nil + default: + y := v + if err := RoundTrip(&y); err != nil { + return nil, err + } + return y, nil + } +} + +func markSeen(seen map[uintptr]struct{}, ptr uintptr) map[uintptr]struct{} { + if seen == nil { + seen = map[uintptr]struct{}{} + } + seen[ptr] = struct{}{} + return seen +} + // Reference returns a pointer to its argument unless the argument already is // a pointer. If the argument is **t, or ***t, etc, it will return *t. // diff --git a/vendor/github.com/open-policy-agent/opa/v1/util/maps.go b/vendor/github.com/open-policy-agent/opa/v1/util/maps.go index b56d57a5f3..bbcb769754 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/util/maps.go +++ b/vendor/github.com/open-policy-agent/opa/v1/util/maps.go @@ -2,6 +2,7 @@ package util import ( "cmp" + "slices" ) // Keys returns a slice of keys from any map. @@ -18,6 +19,22 @@ func KeysSorted[M ~map[K]V, K cmp.Ordered, V any](m M) []K { return Sorted(Keys(m)) } +// KeysSortedFunc returns a slice of keys from any map, sorted by cmp. +func KeysSortedFunc[M ~map[K]V, K comparable, V any](m M, cmp func(K, K) int) []K { + keys := Keys(m) + slices.SortFunc(keys, cmp) + return keys +} + +// MapKeys returns a slice of keys from m, transformed by f. +func MapKeys[M ~map[K]V, K comparable, V, R any](m M, f func(K) R) []R { + r := make([]R, 0, len(m)) + for k := range m { + r = append(r, f(k)) + } + return r +} + // Values returns a slice of values from any map. Copied from golang.org/x/exp/maps. func Values[M ~map[K]V, K comparable, V any](m M) []V { r := make([]V, 0, len(m)) diff --git a/vendor/github.com/open-policy-agent/opa/v1/util/performance.go b/vendor/github.com/open-policy-agent/opa/v1/util/performance.go index 553a4ec113..ea31d16a34 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/util/performance.go +++ b/vendor/github.com/open-policy-agent/opa/v1/util/performance.go @@ -12,6 +12,8 @@ import ( "unsafe" ) +var emptyByteSlice = []byte{} + // SyncPool is a generic sync.Pool for type T, providing some convenience // over sync.Pool directly: [SyncPool.Put] ensures that nil values are not // put into the pool, and [SyncPool.Get] returns a pointer to T without having @@ -112,7 +114,7 @@ func StringToByteSlice[T ~string](s T) []byte { // unsafe.StringData's return value is unspecified for the empty string, // so don't build a slice on top of it. Doing so currently yields a nil // slice, which callers may treat differently from an empty one. - return []byte{} + return emptyByteSlice } return unsafe.Slice(unsafe.StringData(string(s)), len(s)) } diff --git a/vendor/github.com/open-policy-agent/opa/v1/util/slices.go b/vendor/github.com/open-policy-agent/opa/v1/util/slices.go index 21864ea5be..6184feb15c 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/util/slices.go +++ b/vendor/github.com/open-policy-agent/opa/v1/util/slices.go @@ -55,14 +55,15 @@ func TryMap[T any, U any](s []T, f func(T) (U, error)) (r []U, err error) { return r, nil } -// ToSliceOfAny converts a slice of any type T to []any. -func ToSliceOfAny[T any](s []T) []any { +// ToSliceOf converts a slice of T to slice of R, via round-trip +// through any. Needless to say, T must be assignable to R. +func ToSliceOf[R, T any](s []T) []R { if s == nil { return nil } - r := make([]any, len(s)) + r := make([]R, len(s)) for i, v := range s { - r[i] = v + r[i] = any(v).(R) } return r } diff --git a/vendor/github.com/open-policy-agent/opa/v1/version/version.go b/vendor/github.com/open-policy-agent/opa/v1/version/version.go index 6befa5a594..361d1dba93 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/version/version.go +++ b/vendor/github.com/open-policy-agent/opa/v1/version/version.go @@ -10,7 +10,7 @@ import ( "runtime/debug" ) -var Version = "1.20.1" +var Version = "1.21.1" // GoVersion is the version of Go this was built with var GoVersion = runtime.Version() diff --git a/vendor/github.com/sirupsen/logrus/CHANGELOG.md b/vendor/github.com/sirupsen/logrus/CHANGELOG.md index 650f64988f..683cec9088 100644 --- a/vendor/github.com/sirupsen/logrus/CHANGELOG.md +++ b/vendor/github.com/sirupsen/logrus/CHANGELOG.md @@ -2,6 +2,21 @@ All notable changes to this project will be documented in this file. +## 1.10.2 + +Changed: + + * Update `github.com/stretchr/testify` to v1.12.1, removing the legacy + `gopkg.in/yaml.v3` dependency. + +## 1.10.1 + +Fixes: + + * Fix a regression introduced in v1.10.0 where `TextFormatter` could panic + when formatting nil or panicking `error` and `fmt.Stringer` values. + * Allow function-backed implementations of `error` as field values. + ## 1.10.0 Fixes: diff --git a/vendor/github.com/sirupsen/logrus/entry.go b/vendor/github.com/sirupsen/logrus/entry.go index 1cb4b5204c..82de41f9f8 100644 --- a/vendor/github.com/sirupsen/logrus/entry.go +++ b/vendor/github.com/sirupsen/logrus/entry.go @@ -159,15 +159,7 @@ func (entry *Entry) String() (string, error) { // WithError adds an error as single field (using the key defined in [ErrorKey]) // to the Entry. func (entry *Entry) WithError(err error) *Entry { - // Avoid reflection work in WithFields; we know the type is an error; - // copy the entry data and set the ErrorKey directly. - dup := entry.dup() - dup.Data = maps.Clone(entry.Data) - if dup.Data == nil { - dup.Data = make(Fields, 1) - } - dup.Data[ErrorKey] = err - return dup + return entry.WithField(ErrorKey, err) } // WithContext adds a context to the Entry. @@ -182,18 +174,7 @@ func (entry *Entry) WithContext(ctx context.Context) *Entry { func (entry *Entry) WithField(key string, value any) *Entry { dup := entry.dup() dup.Data = maps.Clone(entry.Data) - if isInvalidField(value) { - if dup.err != "" { - dup.err += ", skipping unsupported field " + strconv.Quote(key) - } else { - dup.err = "skipping unsupported field " + strconv.Quote(key) - } - return dup - } - if dup.Data == nil { - dup.Data = make(Fields, 1) - } - dup.Data[key] = value + dup.addField(key, value) return dup } @@ -204,27 +185,11 @@ func (entry *Entry) WithFields(fields Fields) *Entry { maps.Copy(dup.Data, entry.Data) for key, value := range fields { - if isInvalidField(value) { - if dup.err != "" { - dup.err += ", skipping unsupported field " + strconv.Quote(key) - } else { - dup.err = "skipping unsupported field " + strconv.Quote(key) - } - } else { - dup.Data[key] = value - } + dup.addField(key, value) } return dup } -func isInvalidField(v any) bool { - t := reflect.TypeOf(v) - if t == nil { - return false - } - return t.Kind() == reflect.Func || t.Kind() == reflect.Pointer && t.Elem().Kind() == reflect.Func -} - // WithTime overrides the time of the Entry. func (entry *Entry) WithTime(t time.Time) *Entry { dup := entry.dup() @@ -233,6 +198,25 @@ func (entry *Entry) WithTime(t time.Time) *Entry { return dup } +func (entry *Entry) addField(key string, value any) { + if _, ok := value.(error); !ok { + t := reflect.TypeOf(value) + if t != nil && (t.Kind() == reflect.Func || t.Kind() == reflect.Pointer && t.Elem().Kind() == reflect.Func) { + if entry.err != "" { + entry.err += ", skipping unsupported field " + strconv.Quote(key) + } else { + entry.err = "skipping unsupported field " + strconv.Quote(key) + } + return + } + } + + if entry.Data == nil { + entry.Data = make(Fields, 1) + } + entry.Data[key] = value +} + // getPackageName reduces a fully qualified function name to the package name // There really ought to be a better way... func getPackageName(f string) string { diff --git a/vendor/github.com/sirupsen/logrus/exported.go b/vendor/github.com/sirupsen/logrus/exported.go index 844691a941..8b261c124d 100644 --- a/vendor/github.com/sirupsen/logrus/exported.go +++ b/vendor/github.com/sirupsen/logrus/exported.go @@ -55,7 +55,7 @@ func AddHook(hook Hook) { // WithError creates an entry from the standard logger and adds an error to it, // using the value defined in [ErrorKey] as key. func WithError(err error) *Entry { - return std.WithField(ErrorKey, err) + return std.WithError(err) } // WithContext creates an entry from the standard logger and adds a context to it. diff --git a/vendor/github.com/sirupsen/logrus/text_formatter.go b/vendor/github.com/sirupsen/logrus/text_formatter.go index 9b0395adf0..82c1f3da24 100644 --- a/vendor/github.com/sirupsen/logrus/text_formatter.go +++ b/vendor/github.com/sirupsen/logrus/text_formatter.go @@ -5,6 +5,7 @@ import ( "fmt" "maps" "os" + "reflect" "runtime" "slices" "strconv" @@ -329,10 +330,10 @@ func (f *TextFormatter) appendValue(b *bytes.Buffer, value any) { f.appendBytes(b, strconv.AppendBool(raw[:0], v)) return case error: - f.appendString(b, v.Error()) + f.appendError(b, v) return case fmt.Stringer: - f.appendString(b, v.String()) + f.appendStringer(b, v) return } @@ -417,6 +418,29 @@ func (f *TextFormatter) appendNumeric(b *bytes.Buffer, out []byte) { b.Write(out) } +func (f *TextFormatter) appendError(b *bytes.Buffer, v error) { + defer f.recoverValue(b, v, "Error") + + f.appendString(b, v.Error()) +} + +func (f *TextFormatter) appendStringer(b *bytes.Buffer, v fmt.Stringer) { + defer f.recoverValue(b, v, "String") + + f.appendString(b, v.String()) +} + +func (f *TextFormatter) recoverValue(b *bytes.Buffer, v any, method string) { + if r := recover(); r != nil { + rv := reflect.ValueOf(v) + if rv.Kind() == reflect.Pointer && rv.IsNil() { + f.appendString(b, "") + } else { + f.appendString(b, fmt.Sprintf("%%!v(PANIC=%s method: %v)", method, r)) + } + } +} + // needsQuoting returns true if the string contains any byte that // requires quoting. It returns false when every byte is "safe" according // to isSafeByte. diff --git a/vendor/github.com/vektah/gqlparser/v2/ast/value.go b/vendor/github.com/vektah/gqlparser/v2/ast/value.go index 83b471633d..a2f28db9ef 100644 --- a/vendor/github.com/vektah/gqlparser/v2/ast/value.go +++ b/vendor/github.com/vektah/gqlparser/v2/ast/value.go @@ -42,6 +42,18 @@ type ChildValue struct { Comment *CommentGroup } +// isUnsetVariable reports whether v is a variable reference with no supplied +// value and no default — it should be treated as absent, not null. +func (v *Value) isUnsetVariable(vars map[string]any) bool { + if v.Kind != Variable { + return false + } + if _, ok := vars[v.Raw]; ok { + return false + } + return v.VariableDefinition == nil || v.VariableDefinition.DefaultValue == nil +} + func (v *Value) Value(vars map[string]any) (any, error) { if v == nil { return nil, nil @@ -78,6 +90,9 @@ func (v *Value) Value(vars map[string]any) (any, error) { case ObjectValue: val := map[string]any{} for _, elem := range v.Children { + if elem.Value.isUnsetVariable(vars) { + continue + } elemVal, err := elem.Value.Value(vars) if err != nil { return val, err diff --git a/vendor/github.com/vektah/gqlparser/v2/gqlerror/error.go b/vendor/github.com/vektah/gqlparser/v2/gqlerror/error.go index b2ba01bbe5..0615f3d266 100644 --- a/vendor/github.com/vektah/gqlparser/v2/gqlerror/error.go +++ b/vendor/github.com/vektah/gqlparser/v2/gqlerror/error.go @@ -1,6 +1,7 @@ package gqlerror import ( + "encoding/json" "errors" "fmt" "strconv" @@ -35,6 +36,202 @@ type Location struct { Column int `json:"column,omitempty"` } +// SourceLocation pairs a GraphQL line and column with its source document. +// Source is nil when the location has no source document. +type SourceLocation struct { + Line int `json:"line,omitempty"` + Column int `json:"column,omitempty"` + Source *ast.Source `json:"-"` +} + +// ErrorWithSources is the source-aware validation error returned by the +// opt-in validator API. It retains the standard GraphQL error fields while +// Locations stores each location together with its source document. Source is +// omitted from JSON, leaving the standard GraphQL line and column fields. +type ErrorWithSources struct { + Err error `json:"-"` + Message string `json:"message"` + Path ast.Path `json:"path,omitempty"` + Locations []SourceLocation `json:"locations,omitempty"` + Extensions map[string]any `json:"extensions,omitempty"` + Rule string `json:"-"` + + legacyLocations bool +} + +// NewErrorWithSources pairs an existing GraphQL error with source-aware +// locations. The location slice is copied so callers cannot change the error's +// source associations by mutating their input slice. +func NewErrorWithSources(err *Error, locations []SourceLocation) *ErrorWithSources { + if err == nil { + return nil + } + legacyLocations := locations == nil + if len(locations) > 0 && len(err.Locations) > 0 { + if len(locations) != len(err.Locations) { + panic(fmt.Sprintf( + "gqlerror: source location count %d does not match location count %d", + len(locations), + len(err.Locations), + )) + } + for i, location := range err.Locations { + if locations[i].Line != location.Line || locations[i].Column != location.Column { + panic(fmt.Sprintf( + "gqlerror: source location %d does not match location coordinates", + i, + )) + } + } + } + if len(locations) == 0 && len(err.Locations) > 0 { + locations = make([]SourceLocation, len(err.Locations)) + for i, location := range err.Locations { + locations[i] = SourceLocation{ + Line: location.Line, + Column: location.Column, + } + } + } + return &ErrorWithSources{ + Err: err.Err, + Message: err.Message, + Path: err.Path, + Locations: append([]SourceLocation(nil), locations...), + Extensions: err.Extensions, + Rule: err.Rule, + legacyLocations: legacyLocations, + } +} + +// UnmarshalJSON discards source documents because GraphQL error JSON does not +// encode them. +func (err *ErrorWithSources) UnmarshalJSON(data []byte) error { + type errorWithoutMethods ErrorWithSources + err.Locations = nil + err.legacyLocations = true + return json.Unmarshal(data, (*errorWithoutMethods)(err)) +} + +func (err *ErrorWithSources) Error() string { + if err == nil { + return "" + } + locations := make([]Location, len(err.Locations)) + for i, sourceLocation := range err.Locations { + locations[i] = Location{ + Line: sourceLocation.Line, + Column: sourceLocation.Column, + } + } + base := &Error{ + Err: err.Err, + Message: err.Message, + Path: err.Path, + Locations: locations, + Extensions: cloneExtensions(err.Extensions), + Rule: err.Rule, + } + if base.Extensions == nil { + base.Extensions = map[string]any{} + } + filename, _ := base.Extensions["file"].(string) + if len(err.Locations) == 1 { + if filename == "" { + if source := err.Locations[0].Source; source != nil && source.Name != "" { + filename = source.Name + } + } + } else if len(err.Locations) > 1 { + if source := err.Locations[0].Source; source != nil { + filename = source.Name + } else if !err.legacyLocations { + filename = "" + } + } + if filename != "" { + base.Extensions["file"] = filename + } else { + delete(base.Extensions, "file") + } + return base.Error() +} + +func cloneExtensions(extensions map[string]any) map[string]any { + if extensions == nil { + return nil + } + clone := make(map[string]any, len(extensions)) + for key, value := range extensions { + clone[key] = value + } + return clone +} + +func (err *ErrorWithSources) Unwrap() error { + if err == nil { + return nil + } + return err.Err +} + +func (err *ErrorWithSources) AsError() error { + if err == nil { + return nil + } + return err +} + +// SourceLocations returns a shallow copy of the source-aware locations in +// validation order. +func (err *ErrorWithSources) SourceLocations() []SourceLocation { + if err == nil || len(err.Locations) == 0 { + return nil + } + locations := make([]SourceLocation, len(err.Locations)) + copy(locations, err.Locations) + return locations +} + +// SourceList is the result type returned by the opt-in source-aware validator +// APIs. +type SourceList []*ErrorWithSources + +func (errs SourceList) Error() string { + var buf strings.Builder + for _, err := range errs { + buf.WriteString(err.Error()) + buf.WriteByte('\n') + } + return buf.String() +} + +func (errs SourceList) Is(target error) bool { + for _, err := range errs { + if errors.Is(err, target) { + return true + } + } + return false +} + +func (errs SourceList) As(target any) bool { + for _, err := range errs { + if errors.As(err, target) { + return true + } + } + return false +} + +func (errs SourceList) Unwrap() []error { + l := make([]error, len(errs)) + for i, err := range errs { + l[i] = err + } + return l +} + type List []*Error func (err *Error) Error() string { diff --git a/vendor/github.com/vektah/gqlparser/v2/validator/core/helpers.go b/vendor/github.com/vektah/gqlparser/v2/validator/core/helpers.go index f977d00a81..963eba6eaa 100644 --- a/vendor/github.com/vektah/gqlparser/v2/validator/core/helpers.go +++ b/vendor/github.com/vektah/gqlparser/v2/validator/core/helpers.go @@ -28,6 +28,10 @@ func At(position *ast.Position) ErrorOption { Line: position.Line, Column: position.Column, }) + recordSourceLocation(err, gqlerror.Location{ + Line: position.Line, + Column: position.Column, + }, position.Src) if position.Src.Name != "" { err.SetFile(position.Src.Name) } diff --git a/vendor/github.com/vektah/gqlparser/v2/validator/core/source_capture.go b/vendor/github.com/vektah/gqlparser/v2/validator/core/source_capture.go new file mode 100644 index 0000000000..3cc3b1ef1e --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/v2/validator/core/source_capture.go @@ -0,0 +1,82 @@ +package core + +import ( + "fmt" + "sync" + + "github.com/vektah/gqlparser/v2/ast" + "github.com/vektah/gqlparser/v2/gqlerror" +) + +type sourceCapture struct { + locations []gqlerror.SourceLocation +} + +// sourceCaptures bridges the legacy ErrorOption API: At receives only an +// *gqlerror.Error and cannot access CaptureSourceLocations' local capture. +// Entries exist only while source-aware options are being applied. +var sourceCaptures sync.Map // map[*gqlerror.Error]*sourceCapture + +// CaptureSourceLocations applies error options while recording the source +// documents supplied to At. It returns source-aware locations in the same +// order as err.Locations. +// Source-aware validation uses this helper; the regular validation API does +// not install a capture and keeps its existing behavior. +func CaptureSourceLocations(err *gqlerror.Error, apply func()) []gqlerror.SourceLocation { + if err == nil { + panic("gqlparser: cannot capture source locations for a nil error") + } + capture := &sourceCapture{} + sourceCaptures.Store(err, capture) + defer sourceCaptures.Delete(err) + + apply() + if len(err.Locations) == 0 { + if len(capture.locations) > 0 { + panic(fmt.Sprintf( + "gqlparser: captured source location %d does not match the final error locations", + 0, + )) + } + return nil + } + locations := make([]gqlerror.SourceLocation, len(err.Locations)) + for i, location := range err.Locations { + locations[i] = gqlerror.SourceLocation{ + Line: location.Line, + Column: location.Column, + } + } + recorded := 0 + for i, location := range err.Locations { + if recorded == len(capture.locations) { + break + } + captured := capture.locations[recorded] + if captured.Line != location.Line || captured.Column != location.Column { + continue + } + locations[i].Source = captured.Source + recorded++ + } + if recorded != len(capture.locations) { + panic(fmt.Sprintf( + "gqlparser: captured source location %d does not match the final error locations", + recorded, + )) + } + return locations +} + +func recordSourceLocation(err *gqlerror.Error, location gqlerror.Location, source *ast.Source) { + value, ok := sourceCaptures.Load(err) + if !ok { + return + } + capture := value.(*sourceCapture) + capture.locations = append(capture.locations, gqlerror.SourceLocation{ + Line: location.Line, + Column: location.Column, + Source: source, + }) +} diff --git a/vendor/github.com/vektah/gqlparser/v2/validator/schema.go b/vendor/github.com/vektah/gqlparser/v2/validator/schema.go index f8d9472754..3a0d1377a5 100644 --- a/vendor/github.com/vektah/gqlparser/v2/validator/schema.go +++ b/vendor/github.com/vektah/gqlparser/v2/validator/schema.go @@ -189,6 +189,10 @@ func ValidateSchemaDocument(sd *SchemaDocument) (*Schema, error) { return nil, err } + if err := validateInputObjectCircularRefs(&schema); err != nil { + return nil, err + } + if err := validateDirectiveDefinitions(&schema); err != nil { return nil, err } @@ -245,6 +249,75 @@ func validateTypeDefinitions(schema *Schema) *gqlerror.Error { return nil } +// validateInputObjectCircularRefs rejects input object cycles of non-null fields. +// https://spec.graphql.org/October2021/#sec-Input-Objects.Circular-References +func validateInputObjectCircularRefs(schema *Schema) *gqlerror.Error { + types := make([]string, 0, len(schema.Types)) + for typ := range schema.Types { + types = append(types, typ) + } + sort.Strings(types) + + // A type still on fieldPath means a cycle. A visited type was already checked. + visited := make(map[string]bool, len(schema.Types)) + fieldPath := make([]*FieldDefinition, 0, len(schema.Types)) + fieldPathIndexByTypeName := make(map[string]int, len(schema.Types)) + + var detectCycle func(def *Definition) *gqlerror.Error + detectCycle = func(def *Definition) *gqlerror.Error { + if visited[def.Name] { + return nil + } + visited[def.Name] = true + fieldPathIndexByTypeName[def.Name] = len(fieldPath) + + for _, field := range def.Fields { + // A nullable field or any list breaks the chain. + if !field.Type.NonNull || field.Type.NamedType == "" { + continue + } + fieldType := schema.Types[field.Type.NamedType] + if fieldType == nil || fieldType.Kind != InputObject { + continue + } + + fieldPath = append(fieldPath, field) + if cycleIndex, ok := fieldPathIndexByTypeName[fieldType.Name]; ok { + cyclePath := fieldPath[cycleIndex:] + fieldNames := make([]string, len(cyclePath)) + for i, cycleField := range cyclePath { + fieldNames[i] = cycleField.Name + } + return gqlerror.ErrorPosf( + cyclePath[0].Position, + "Cannot reference Input Object %s within itself through "+ + "a series of non-null fields: %s.", + strconv.Quote(fieldType.Name), + strconv.Quote(strings.Join(fieldNames, ".")), + ) + } + if err := detectCycle(fieldType); err != nil { + return err + } + fieldPath = fieldPath[:len(fieldPath)-1] + } + + delete(fieldPathIndexByTypeName, def.Name) + return nil + } + + for _, typ := range types { + def := schema.Types[typ] + if def.Kind != InputObject { + continue + } + if err := detectCycle(def); err != nil { + return err + } + } + return nil +} + func validateDirectiveDefinitions(schema *Schema) *gqlerror.Error { directives := make([]string, 0, len(schema.Directives)) for directive := range schema.Directives { diff --git a/vendor/github.com/vektah/gqlparser/v2/validator/schema_test.yml b/vendor/github.com/vektah/gqlparser/v2/validator/schema_test.yml index b66e1e3d5a..589df95cc2 100644 --- a/vendor/github.com/vektah/gqlparser/v2/validator/schema_test.yml +++ b/vendor/github.com/vektah/gqlparser/v2/validator/schema_test.yml @@ -386,6 +386,116 @@ inputs: message: 'UNION a: field must be one of SCALAR, ENUM, INPUT_OBJECT.' locations: [{line: 3, column: 13}] + - name: cannot reference itself through a non-null field + input: | + input T { + self: T! + } + error: + message: 'Cannot reference Input Object "T" within itself through a series of non-null fields: "self".' + locations: [{line: 2, column: 3}] + + - name: cannot reference itself through a chain of non-null fields + input: | + input A { + startLoop: B! + } + input B { + nextInLoop: C! + } + input C { + closeLoop: A! + } + error: + message: 'Cannot reference Input Object "A" within itself through a series of non-null fields: "startLoop.nextInLoop.closeLoop".' + locations: [{line: 2, column: 3}] + + - name: cannot reference itself through a non-null field added by an extension + input: | + input T { + id: ID + } + extend input T { + self: T! + } + error: + message: 'Cannot reference Input Object "T" within itself through a series of non-null fields: "self".' + locations: [{line: 5, column: 3}] + + - name: cannot reference itself through a non-null field on a nested cycle + input: | + input Outer { + inner: Inner! + } + input Inner { + self: Inner! + } + error: + message: 'Cannot reference Input Object "Inner" within itself through a series of non-null fields: "self".' + locations: [{line: 5, column: 3}] + + - name: cannot reference itself when an earlier input object is cycle free + input: | + input Entry { + maybe: Loop + } + input Loop { + self: Loop! + } + error: + message: 'Cannot reference Input Object "Loop" within itself through a series of non-null fields: "self".' + locations: [{line: 5, column: 3}] + + - name: can reference itself through a nullable field + input: | + input T { + self: T + } + + - name: can reference itself through a list field + input: | + input T { + a: [T!]! + b: [T]! + c: [T!] + d: [T] + e: [[T!]!]! + } + + - name: can reference itself when the chain is broken by a list + input: | + input A { + startLoop: B! + } + input B { + loopBack: [A!]! + } + + - name: can reference itself when the chain is broken by a nullable field + input: | + input A { + startLoop: B! + } + input B { + nextInLoop: C + } + input C { + closeLoop: A! + } + + - name: can reference the same input object twice without a cycle + input: | + input A { + b: B! + c: C! + } + input B { + c: C! + } + input C { + id: ID! + } + args: - name: Valid arg types input: | diff --git a/vendor/github.com/vektah/gqlparser/v2/validator/validator.go b/vendor/github.com/vektah/gqlparser/v2/validator/validator.go index 9fb40d6a18..8efacf6769 100644 --- a/vendor/github.com/vektah/gqlparser/v2/validator/validator.go +++ b/vendor/github.com/vektah/gqlparser/v2/validator/validator.go @@ -118,6 +118,16 @@ func Validate(schema *Schema, doc *QueryDocument, rules ...Rule) gqlerror.List { return errs } +// ValidateWithSources is the source-aware counterpart to Validate. It keeps +// source documents for every location recorded by the built-in At option while +// leaving Error and the regular validation API unchanged. +func ValidateWithSources(schema *Schema, doc *QueryDocument, rules ...Rule) gqlerror.SourceList { + if rules == nil { + rules = specifiedRules + } + return validateWithSources(schema, doc, rules) +} + func ValidateWithRules( schema *Schema, doc *QueryDocument, @@ -161,3 +171,59 @@ func ValidateWithRules( Walk(schema, doc, observers) return errs } + +// ValidateWithRulesWithSources is the source-aware counterpart to +// ValidateWithRules. +func ValidateWithRulesWithSources( + schema *Schema, + doc *QueryDocument, + rules *validatorrules.Rules, +) gqlerror.SourceList { + if rules == nil { + rules = validatorrules.NewDefaultRules() + } + + var currentRules []Rule //nolint:prealloc // would require extra local refs for len + for name, ruleFunc := range rules.GetInner() { + currentRules = append(currentRules, Rule{Name: name, RuleFunc: ruleFunc}) + // ensure deterministic order evaluation + sort.Sort(core.NameSorter(currentRules)) + } + return validateWithSources(schema, doc, currentRules) +} + +func validateWithSources(schema *Schema, doc *QueryDocument, rules []Rule) gqlerror.SourceList { + var errs gqlerror.SourceList + if schema == nil { + errs = append(errs, gqlerror.NewErrorWithSources( + gqlerror.Errorf("cannot validate as Schema is nil"), + nil, + )) + } + if doc == nil { + errs = append(errs, gqlerror.NewErrorWithSources( + gqlerror.Errorf("cannot validate as QueryDocument is nil"), + nil, + )) + } + if len(errs) > 0 { + return errs + } + + observers := &core.Events{} + for i := range rules { + rule := rules[i] + rule.RuleFunc(observers, func(options ...ErrorOption) { + err := &gqlerror.Error{Rule: rule.Name} + sources := core.CaptureSourceLocations(err, func() { + for _, option := range options { + option(err) + } + }) + errs = append(errs, gqlerror.NewErrorWithSources(err, sources)) + }) + } + + Walk(schema, doc, observers) + return errs +} diff --git a/vendor/go.yaml.in/yaml/v2/.travis.yml b/vendor/go.yaml.in/yaml/v2/.travis.yml deleted file mode 100644 index 7348c50c0c..0000000000 --- a/vendor/go.yaml.in/yaml/v2/.travis.yml +++ /dev/null @@ -1,17 +0,0 @@ -language: go - -go: - - "1.4.x" - - "1.5.x" - - "1.6.x" - - "1.7.x" - - "1.8.x" - - "1.9.x" - - "1.10.x" - - "1.11.x" - - "1.12.x" - - "1.13.x" - - "1.14.x" - - "tip" - -go_import_path: gopkg.in/yaml.v2 diff --git a/vendor/go.yaml.in/yaml/v2/LICENSE b/vendor/go.yaml.in/yaml/v2/LICENSE deleted file mode 100644 index 8dada3edaf..0000000000 --- a/vendor/go.yaml.in/yaml/v2/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vendor/go.yaml.in/yaml/v2/LICENSE.libyaml b/vendor/go.yaml.in/yaml/v2/LICENSE.libyaml deleted file mode 100644 index 8da58fbf6f..0000000000 --- a/vendor/go.yaml.in/yaml/v2/LICENSE.libyaml +++ /dev/null @@ -1,31 +0,0 @@ -The following files were ported to Go from C files of libyaml, and thus -are still covered by their original copyright and license: - - apic.go - emitterc.go - parserc.go - readerc.go - scannerc.go - writerc.go - yamlh.go - yamlprivateh.go - -Copyright (c) 2006 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. diff --git a/vendor/go.yaml.in/yaml/v2/NOTICE b/vendor/go.yaml.in/yaml/v2/NOTICE deleted file mode 100644 index 866d74a7ad..0000000000 --- a/vendor/go.yaml.in/yaml/v2/NOTICE +++ /dev/null @@ -1,13 +0,0 @@ -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. diff --git a/vendor/go.yaml.in/yaml/v2/README.md b/vendor/go.yaml.in/yaml/v2/README.md deleted file mode 100644 index c9388da425..0000000000 --- a/vendor/go.yaml.in/yaml/v2/README.md +++ /dev/null @@ -1,131 +0,0 @@ -# YAML support for the Go language - -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.1 and 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 *go.yaml.in/yaml/v2*. - -To install it, run: - - go get go.yaml.in/yaml/v2 - -API documentation ------------------ - -See: - -API stability -------------- - -The package API for yaml v2 will remain stable as described in [gopkg.in](https://gopkg.in). - - -License -------- - -The yaml package is licensed under the Apache License 2.0. Please see the LICENSE file for details. - - -Example -------- - -```Go -package main - -import ( - "fmt" - "log" - - "go.yaml.in/yaml/v2" -) - -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 -``` - diff --git a/vendor/go.yaml.in/yaml/v2/apic.go b/vendor/go.yaml.in/yaml/v2/apic.go deleted file mode 100644 index acf71402cf..0000000000 --- a/vendor/go.yaml.in/yaml/v2/apic.go +++ /dev/null @@ -1,744 +0,0 @@ -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 -} - -var disableLineWrapping = false - -// 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), - } - if disableLineWrapping { - emitter.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. -// */ -// -//YAML_DECLARE(int) -//yaml_alias_event_initialize(event *yaml_event_t, anchor *yaml_char_t) -//{ -// mark yaml_mark_t = { 0, 0, 0 } -// anchor_copy *yaml_char_t = NULL -// -// assert(event) // Non-NULL event object is expected. -// assert(anchor) // Non-NULL anchor is expected. -// -// if (!yaml_check_utf8(anchor, strlen((char *)anchor))) return 0 -// -// anchor_copy = yaml_strdup(anchor) -// if (!anchor_copy) -// return 0 -// -// ALIAS_EVENT_INIT(*event, anchor_copy, mark, mark) -// -// return 1 -//} - -// 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 -//} -// -// diff --git a/vendor/go.yaml.in/yaml/v2/decode.go b/vendor/go.yaml.in/yaml/v2/decode.go deleted file mode 100644 index 129bc2a97d..0000000000 --- a/vendor/go.yaml.in/yaml/v2/decode.go +++ /dev/null @@ -1,815 +0,0 @@ -package yaml - -import ( - "encoding" - "encoding/base64" - "fmt" - "io" - "math" - "reflect" - "strconv" - "time" -) - -const ( - documentNode = 1 << iota - mappingNode - sequenceNode - scalarNode - aliasNode -) - -type node struct { - kind int - line, column int - tag string - // For an alias node, alias holds the resolved alias. - alias *node - value string - implicit bool - children []*node - anchors map[string]*node -} - -// ---------------------------------------------------------------------------- -// Parser, produces a node tree out of a libyaml event stream. - -type parser struct { - parser yaml_parser_t - event yaml_event_t - doc *node - doneInit 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.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 - } - if !yaml_parser_parse(&p.parser, &p.event) { - p.fail() - } - return p.event.typ -} - -func (p *parser) fail() { - var where string - var line int - 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++ - } - } else if p.parser.context_mark.line != 0 { - line = p.parser.context_mark.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 { - p.doc.anchors[string(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 - default: - panic("attempted to parse unknown event: " + p.event.typ.String()) - } -} - -func (p *parser) node(kind int) *node { - return &node{ - kind: kind, - line: p.event.start_mark.line, - column: p.event.start_mark.column, - } -} - -func (p *parser) document() *node { - n := p.node(documentNode) - n.anchors = make(map[string]*node) - p.doc = n - p.expect(yaml_DOCUMENT_START_EVENT) - n.children = append(n.children, p.parse()) - p.expect(yaml_DOCUMENT_END_EVENT) - return n -} - -func (p *parser) alias() *node { - n := p.node(aliasNode) - n.value = string(p.event.anchor) - n.alias = p.doc.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 { - n := p.node(scalarNode) - n.value = string(p.event.value) - n.tag = string(p.event.tag) - n.implicit = p.event.implicit - p.anchor(n, p.event.anchor) - p.expect(yaml_SCALAR_EVENT) - return n -} - -func (p *parser) sequence() *node { - n := p.node(sequenceNode) - p.anchor(n, p.event.anchor) - p.expect(yaml_SEQUENCE_START_EVENT) - for p.peek() != yaml_SEQUENCE_END_EVENT { - n.children = append(n.children, p.parse()) - } - p.expect(yaml_SEQUENCE_END_EVENT) - return n -} - -func (p *parser) mapping() *node { - n := p.node(mappingNode) - p.anchor(n, p.event.anchor) - p.expect(yaml_MAPPING_START_EVENT) - for p.peek() != yaml_MAPPING_END_EVENT { - n.children = append(n.children, p.parse(), p.parse()) - } - 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 - mapType reflect.Type - terrors []string - strict bool - - decodeCount int - aliasCount int - aliasDepth int -} - -var ( - mapItemType = reflect.TypeOf(MapItem{}) - durationType = reflect.TypeOf(time.Duration(0)) - defaultMapType = reflect.TypeOf(map[interface{}]interface{}{}) - ifaceType = defaultMapType.Elem() - timeType = reflect.TypeOf(time.Time{}) - ptrTimeType = reflect.TypeOf(&time.Time{}) -) - -func newDecoder(strict bool) *decoder { - d := &decoder{mapType: defaultMapType, strict: strict} - 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 != yaml_SEQ_TAG && tag != yaml_MAP_TAG { - 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+1, shortTag(tag), value, out.Type())) -} - -func (d *decoder) callUnmarshaler(n *node, u Unmarshaler) (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.tag == yaml_NULL_TAG || n.kind == scalarNode && n.tag == "" && (n.value == "null" || n.value == "~" || n.value == "" && n.implicit) { - 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() { - if u, ok := out.Addr().Interface().(Unmarshaler); ok { - good = d.callUnmarshaler(n, u) - return out, true, good - } - } - } - return out, false, false -} - -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") - } - 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) - default: - panic("internal error: unknown node kind: " + strconv.Itoa(n.kind)) - } - return good -} - -func (d *decoder) document(n *node, out reflect.Value) (good bool) { - if len(n.children) == 1 { - d.doc = n - d.unmarshal(n.children[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) scalar(n *node, out reflect.Value) bool { - var tag string - var resolved interface{} - if n.tag == "" && !n.implicit { - tag = yaml_STR_TAG - resolved = n.value - } else { - tag, resolved = resolve(n.tag, n.value) - if tag == yaml_BINARY_TAG { - data, err := base64.StdEncoding.DecodeString(resolved.(string)) - if err != nil { - failf("!!binary value contains invalid base64 data") - } - resolved = string(data) - } - } - if resolved == nil { - if out.Kind() == reflect.Map && !out.CanAddr() { - resetMap(out) - } else { - out.Set(reflect.Zero(out.Type())) - } - return true - } - 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 == yaml_BINARY_TAG { - 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 == yaml_BINARY_TAG { - out.SetString(resolved.(string)) - return true - } - if resolved != nil { - out.SetString(n.value) - return true - } - case reflect.Interface: - if resolved == nil { - out.Set(reflect.Zero(out.Type())) - } else if tag == yaml_TIMESTAMP_TAG { - // It looks like a timestamp but for backward compatibility - // reasons we set it as a string, so that code that unmarshals - // timestamp-like values into interface{} will continue to - // see a string and not a time.Time. - // TODO(v3) Drop this. - out.Set(reflect.ValueOf(n.value)) - } else { - out.Set(reflect.ValueOf(resolved)) - } - return true - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - switch resolved := resolved.(type) { - case int: - if !out.OverflowInt(int64(resolved)) { - out.SetInt(int64(resolved)) - return true - } - case int64: - if !out.OverflowInt(resolved) { - out.SetInt(resolved) - return true - } - case uint64: - if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { - out.SetInt(int64(resolved)) - return true - } - case float64: - if 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 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: - if out.Type().Elem() == reflect.TypeOf(resolved) { - // TODO DOes this make sense? When is out a Ptr except when decoding a nil value? - elem := reflect.New(out.Type().Elem()) - elem.Elem().Set(reflect.ValueOf(resolved)) - out.Set(elem) - return true - } - } - 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.children) - - 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, yaml_SEQ_TAG, out) - return false - } - et := out.Type().Elem() - - j := 0 - for i := 0; i < l; i++ { - e := reflect.New(et).Elem() - if ok := d.unmarshal(n.children[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) { - switch out.Kind() { - case reflect.Struct: - return d.mappingStruct(n, out) - case reflect.Slice: - return d.mappingSlice(n, out) - case reflect.Map: - // okay - case reflect.Interface: - if d.mapType.Kind() == reflect.Map { - iface := out - out = reflect.MakeMap(d.mapType) - iface.Set(out) - } else { - slicev := reflect.New(d.mapType).Elem() - if !d.mappingSlice(n, slicev) { - return false - } - out.Set(slicev) - return true - } - default: - d.terror(n, yaml_MAP_TAG, out) - return false - } - outt := out.Type() - kt := outt.Key() - et := outt.Elem() - - mapType := d.mapType - if outt.Key() == ifaceType && outt.Elem() == ifaceType { - d.mapType = outt - } - - if out.IsNil() { - out.Set(reflect.MakeMap(outt)) - } - l := len(n.children) - for i := 0; i < l; i += 2 { - if isMerge(n.children[i]) { - d.merge(n.children[i+1], out) - continue - } - k := reflect.New(kt).Elem() - if d.unmarshal(n.children[i], k) { - 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.unmarshal(n.children[i+1], e) { - d.setMapIndex(n.children[i+1], out, k, e) - } - } - } - d.mapType = mapType - return true -} - -func (d *decoder) setMapIndex(n *node, out, k, v reflect.Value) { - if d.strict && out.MapIndex(k) != zeroValue { - d.terrors = append(d.terrors, fmt.Sprintf("line %d: key %#v already set in map", n.line+1, k.Interface())) - return - } - out.SetMapIndex(k, v) -} - -func (d *decoder) mappingSlice(n *node, out reflect.Value) (good bool) { - outt := out.Type() - if outt.Elem() != mapItemType { - d.terror(n, yaml_MAP_TAG, out) - return false - } - - mapType := d.mapType - d.mapType = outt - - var slice []MapItem - var l = len(n.children) - for i := 0; i < l; i += 2 { - if isMerge(n.children[i]) { - d.merge(n.children[i+1], out) - continue - } - item := MapItem{} - k := reflect.ValueOf(&item.Key).Elem() - if d.unmarshal(n.children[i], k) { - v := reflect.ValueOf(&item.Value).Elem() - if d.unmarshal(n.children[i+1], v) { - slice = append(slice, item) - } - } - } - out.Set(reflect.ValueOf(slice)) - d.mapType = mapType - return true -} - -func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) { - sinfo, err := getStructInfo(out.Type()) - if err != nil { - panic(err) - } - name := settableValueOf("") - l := len(n.children) - - var inlineMap reflect.Value - var elemType reflect.Type - if sinfo.InlineMap != -1 { - inlineMap = out.Field(sinfo.InlineMap) - inlineMap.Set(reflect.New(inlineMap.Type()).Elem()) - elemType = inlineMap.Type().Elem() - } - - var doneFields []bool - if d.strict { - doneFields = make([]bool, len(sinfo.FieldsList)) - } - for i := 0; i < l; i += 2 { - ni := n.children[i] - if isMerge(ni) { - d.merge(n.children[i+1], out) - continue - } - if !d.unmarshal(ni, name) { - continue - } - if info, ok := sinfo.FieldsMap[name.String()]; ok { - if d.strict { - if doneFields[info.Id] { - d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.line+1, name.String(), out.Type())) - continue - } - doneFields[info.Id] = true - } - var field reflect.Value - if info.Inline == nil { - field = out.Field(info.Num) - } else { - field = out.FieldByIndex(info.Inline) - } - d.unmarshal(n.children[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.children[i+1], value) - d.setMapIndex(n.children[i+1], inlineMap, name, value) - } else if d.strict { - d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.line+1, name.String(), out.Type())) - } - } - return true -} - -func failWantMap() { - failf("map merge requires map or sequence of maps as the value") -} - -func (d *decoder) merge(n *node, out reflect.Value) { - switch n.kind { - case mappingNode: - d.unmarshal(n, out) - case aliasNode: - if n.alias != nil && n.alias.kind != mappingNode { - failWantMap() - } - d.unmarshal(n, out) - case sequenceNode: - // Step backwards as earlier nodes take precedence. - for i := len(n.children) - 1; i >= 0; i-- { - ni := n.children[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() - } -} - -func isMerge(n *node) bool { - return n.kind == scalarNode && n.value == "<<" && (n.implicit == true || n.tag == yaml_MERGE_TAG) -} diff --git a/vendor/go.yaml.in/yaml/v2/emitterc.go b/vendor/go.yaml.in/yaml/v2/emitterc.go deleted file mode 100644 index a1c2cc5262..0000000000 --- a/vendor/go.yaml.in/yaml/v2/emitterc.go +++ /dev/null @@ -1,1685 +0,0 @@ -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") - } - emitter.column = 0 - emitter.line++ - 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 - } - emitter.column = 0 - emitter.line++ - } - 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 { - emitter.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) - - case yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE: - return yaml_emitter_emit_flow_sequence_item(emitter, event, false) - - case yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE: - return yaml_emitter_emit_flow_mapping_key(emitter, event, true) - - case yaml_EMIT_FLOW_MAPPING_KEY_STATE: - return yaml_emitter_emit_flow_mapping_key(emitter, event, 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 - - 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 { - if !yaml_emitter_write_indent(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) - return yaml_emitter_emit_node(emitter, event, true, false, false, false) -} - -// 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") - } - 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 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 { - 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_indicator(emitter, []byte{','}, false, false, false) { - return false - } - if !yaml_emitter_write_indent(emitter) { - return false - } - } - if !yaml_emitter_write_indicator(emitter, []byte{']'}, false, false, false) { - return false - } - emitter.state = emitter.states[len(emitter.states)-1] - emitter.states = emitter.states[:len(emitter.states)-1] - - return true - } - - if !first { - if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { - return false - } - } - - if emitter.canonical || emitter.column > emitter.best_width { - if !yaml_emitter_write_indent(emitter) { - return false - } - } - emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE) - return yaml_emitter_emit_node(emitter, event, false, true, false, false) -} - -// Expect a flow key node. -func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first 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 { - 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_indicator(emitter, []byte{','}, false, false, false) { - return false - } - if !yaml_emitter_write_indent(emitter) { - return false - } - } - if !yaml_emitter_write_indicator(emitter, []byte{'}'}, false, false, false) { - return false - } - emitter.state = emitter.states[len(emitter.states)-1] - emitter.states = emitter.states[:len(emitter.states)-1] - return true - } - - if !first { - if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { - 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 - } - } - emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_KEY_STATE) - return yaml_emitter_emit_node(emitter, event, false, false, true, false) -} - -// 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, emitter.mapping_context && !emitter.indention) { - 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_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) - return yaml_emitter_emit_node(emitter, event, false, true, false, false) -} - -// 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 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 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 - } - } - emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_KEY_STATE) - return yaml_emitter_emit_node(emitter, event, false, false, true, false) -} - -// 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") -} - -// 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 - - 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 !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 || special_characters { - emitter.scalar_data.flow_plain_allowed = false - emitter.scalar_data.block_plain_allowed = false - emitter.scalar_data.single_quoted_allowed = false - 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 - - 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 - } - } - for emitter.column < indent { - if !put(emitter, ' ') { - return false - } - } - emitter.whitespace = true - emitter.indention = true - 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 !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 - } - } - - 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 !put_break(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 !put_break(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 -} diff --git a/vendor/go.yaml.in/yaml/v2/encode.go b/vendor/go.yaml.in/yaml/v2/encode.go deleted file mode 100644 index 0ee738e11b..0000000000 --- a/vendor/go.yaml.in/yaml/v2/encode.go +++ /dev/null @@ -1,390 +0,0 @@ -package yaml - -import ( - "encoding" - "fmt" - "io" - "reflect" - "regexp" - "sort" - "strconv" - "strings" - "time" - "unicode/utf8" -) - -// jsonNumber is the interface of the encoding/json.Number datatype. -// Repeating the interface here avoids a dependency on encoding/json, and also -// supports other libraries like jsoniter, which use a similar datatype with -// the same interface. Detecting this interface is useful when dealing with -// structures containing json.Number, which is a string under the hood. The -// encoder should prefer the use of Int64(), Float64() and string(), in that -// order, when encoding this type. -type jsonNumber interface { - Float64() (float64, error) - Int64() (int64, error) - String() string -} - -type encoder struct { - emitter yaml_emitter_t - event yaml_event_t - out []byte - flow bool - // doneInit holds whether the initial stream_start_event has been - // emitted. - 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 - } - 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() - 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) { - if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() { - e.nilv() - return - } - iface := in.Interface() - switch m := iface.(type) { - case jsonNumber: - integer, err := m.Int64() - if err == nil { - // In this case the json.Number is a valid int64 - in = reflect.ValueOf(integer) - break - } - float, err := m.Float64() - if err == nil { - // In this case the json.Number is a valid float64 - in = reflect.ValueOf(float) - break - } - // fallback case - no number could be obtained - in = reflect.ValueOf(m.String()) - case time.Time, *time.Time: - // Although time.Time implements TextMarshaler, - // we don't want to treat it as a string for YAML - // purposes because YAML has special support for - // timestamps. - case Marshaler: - v, err := m.MarshalYAML() - if err != nil { - fail(err) - } - if v == nil { - e.nilv() - return - } - in = reflect.ValueOf(v) - case encoding.TextMarshaler: - text, err := m.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: - if in.Type() == ptrTimeType { - e.timev(tag, in.Elem()) - } else { - e.marshal(tag, in.Elem()) - } - case reflect.Struct: - if in.Type() == timeType { - e.timev(tag, in) - } else { - e.structv(tag, in) - } - case reflect.Slice, reflect.Array: - if in.Type().Elem() == mapItemType { - e.itemsv(tag, in) - } else { - e.slicev(tag, in) - } - case reflect.String: - e.stringv(tag, in) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - if in.Type() == durationType { - e.stringv(tag, reflect.ValueOf(iface.(time.Duration).String())) - } else { - 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) itemsv(tag string, in reflect.Value) { - e.mappingv(tag, func() { - slice := in.Convert(reflect.TypeOf([]MapItem{})).Interface().([]MapItem) - for _, item := range slice { - e.marshal("", reflect.ValueOf(item.Key)) - e.marshal("", reflect.ValueOf(item.Value)) - } - }) -} - -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 = in.FieldByIndex(info.Inline) - } - 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("Can't 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_]*)?$`) - -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 == yaml_BINARY_TAG { - 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 = yaml_BINARY_TAG - 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 == yaml_STR_TAG && !isBase60Float(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"): - style = yaml_LITERAL_SCALAR_STYLE - case canUsePlain: - style = yaml_PLAIN_SCALAR_STYLE - default: - style = yaml_DOUBLE_QUOTED_SCALAR_STYLE - } - e.emitScalar(s, "", tag, style) -} - -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) -} - -func (e *encoder) intv(tag string, in reflect.Value) { - s := strconv.FormatInt(in.Int(), 10) - e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) -} - -func (e *encoder) uintv(tag string, in reflect.Value) { - s := strconv.FormatUint(in.Uint(), 10) - e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) -} - -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) -} - -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) -} - -func (e *encoder) nilv() { - e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE) -} - -func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t) { - implicit := tag == "" - e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style)) - e.emit() -} diff --git a/vendor/go.yaml.in/yaml/v2/parserc.go b/vendor/go.yaml.in/yaml/v2/parserc.go deleted file mode 100644 index 81d05dfe57..0000000000 --- a/vendor/go.yaml.in/yaml/v2/parserc.go +++ /dev/null @@ -1,1095 +0,0 @@ -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) { - return &parser.tokens[parser.tokens_head] - } - return nil -} - -// 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 - - *event = yaml_event_t{ - typ: yaml_DOCUMENT_START_EVENT, - start_mark: token.start_mark, - end_mark: token.end_mark, - } - - } 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, - } - return true -} - -// 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, - } - 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), - } - 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), - } - 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), - } - 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), - } - 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), - } - 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) - 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 - skip_token(parser) - 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 - skip_token(parser) - 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 -} - -// 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) - parser.marks = append(parser.marks, token.start_mark) - skip_token(parser) - } - - token := peek_token(parser) - if token == nil { - return false - } - - 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, - } - 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) - 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, - } - - 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, - } - 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 -} diff --git a/vendor/go.yaml.in/yaml/v2/readerc.go b/vendor/go.yaml.in/yaml/v2/readerc.go deleted file mode 100644 index 7c1f5fac3d..0000000000 --- a/vendor/go.yaml.in/yaml/v2/readerc.go +++ /dev/null @@ -1,412 +0,0 @@ -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 -} diff --git a/vendor/go.yaml.in/yaml/v2/resolve.go b/vendor/go.yaml.in/yaml/v2/resolve.go deleted file mode 100644 index 4120e0c916..0000000000 --- a/vendor/go.yaml.in/yaml/v2/resolve.go +++ /dev/null @@ -1,258 +0,0 @@ -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, yaml_BOOL_TAG, []string{"y", "Y", "yes", "Yes", "YES"}}, - {true, yaml_BOOL_TAG, []string{"true", "True", "TRUE"}}, - {true, yaml_BOOL_TAG, []string{"on", "On", "ON"}}, - {false, yaml_BOOL_TAG, []string{"n", "N", "no", "No", "NO"}}, - {false, yaml_BOOL_TAG, []string{"false", "False", "FALSE"}}, - {false, yaml_BOOL_TAG, []string{"off", "Off", "OFF"}}, - {nil, yaml_NULL_TAG, []string{"", "~", "null", "Null", "NULL"}}, - {math.NaN(), yaml_FLOAT_TAG, []string{".nan", ".NaN", ".NAN"}}, - {math.Inf(+1), yaml_FLOAT_TAG, []string{".inf", ".Inf", ".INF"}}, - {math.Inf(+1), yaml_FLOAT_TAG, []string{"+.inf", "+.Inf", "+.INF"}}, - {math.Inf(-1), yaml_FLOAT_TAG, []string{"-.inf", "-.Inf", "-.INF"}}, - {"<<", yaml_MERGE_TAG, []string{"<<"}}, - } - - m := resolveMap - for _, item := range resolveMapList { - for _, s := range item.l { - m[s] = resolveMapItem{item.v, item.tag} - } - } -} - -const longTagPrefix = "tag:yaml.org,2002:" - -func shortTag(tag string) string { - // TODO This can easily be made faster and produce less garbage. - if strings.HasPrefix(tag, longTagPrefix) { - return "!!" + tag[len(longTagPrefix):] - } - return tag -} - -func longTag(tag string) string { - if strings.HasPrefix(tag, "!!") { - return longTagPrefix + tag[2:] - } - return tag -} - -func resolvableTag(tag string) bool { - switch tag { - case "", yaml_STR_TAG, yaml_BOOL_TAG, yaml_INT_TAG, yaml_FLOAT_TAG, yaml_NULL_TAG, yaml_TIMESTAMP_TAG: - 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{}) { - if !resolvableTag(tag) { - return tag, in - } - - defer func() { - switch tag { - case "", rtag, yaml_STR_TAG, yaml_BINARY_TAG: - return - case yaml_FLOAT_TAG: - if rtag == yaml_INT_TAG { - switch v := out.(type) { - case int64: - rtag = yaml_FLOAT_TAG - out = float64(v) - return - case int: - rtag = yaml_FLOAT_TAG - 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 != yaml_STR_TAG && tag != yaml_BINARY_TAG { - // 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 yaml_FLOAT_TAG, 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 == yaml_TIMESTAMP_TAG { - t, ok := parseTimestamp(in) - if ok { - return yaml_TIMESTAMP_TAG, t - } - } - - plain := strings.Replace(in, "_", "", -1) - intv, err := strconv.ParseInt(plain, 0, 64) - if err == nil { - if intv == int64(int(intv)) { - return yaml_INT_TAG, int(intv) - } else { - return yaml_INT_TAG, intv - } - } - uintv, err := strconv.ParseUint(plain, 0, 64) - if err == nil { - return yaml_INT_TAG, uintv - } - if yamlStyleFloat.MatchString(plain) { - floatv, err := strconv.ParseFloat(plain, 64) - if err == nil { - return yaml_FLOAT_TAG, floatv - } - } - if strings.HasPrefix(plain, "0b") { - intv, err := strconv.ParseInt(plain[2:], 2, 64) - if err == nil { - if intv == int64(int(intv)) { - return yaml_INT_TAG, int(intv) - } else { - return yaml_INT_TAG, intv - } - } - uintv, err := strconv.ParseUint(plain[2:], 2, 64) - if err == nil { - return yaml_INT_TAG, 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 yaml_INT_TAG, int(intv) - } else { - return yaml_INT_TAG, intv - } - } - } - default: - panic("resolveTable item not yet handled: " + string(rune(hint)) + " (with " + in + ")") - } - } - return yaml_STR_TAG, 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 -} diff --git a/vendor/go.yaml.in/yaml/v2/scannerc.go b/vendor/go.yaml.in/yaml/v2/scannerc.go deleted file mode 100644 index 0b9bb6030a..0000000000 --- a/vendor/go.yaml.in/yaml/v2/scannerc.go +++ /dev/null @@ -1,2711 +0,0 @@ -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) { - 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 - } 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]) - } -} - -// Copy a character to a string buffer and advance pointers. -func read(parser *yaml_parser_t, s []byte) []byte { - 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-- - 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 { - if parser.tokens_head != len(parser.tokens) { - // If queue is non-empty, check if any potential simple key may - // occupy the head position. - 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) 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) - } - - // Eat whitespaces and comments until we reach the next token. - if !yaml_parser_scan_to_next_token(parser) { - return false - } - - // Check the indentation level against the current column. - if !yaml_parser_unroll_indent(parser, parser.mark.column) { - 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) - } - - // 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] 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) bool { - // In the flow context, do nothing. - if parser.flow_level > 0 { - return true - } - - // Loop through the indentation levels in the stack. - for parser.indent > column { - // Create a token and append it to the queue. - token := yaml_token_t{ - typ: yaml_BLOCK_END_TOKEN, - start_mark: parser.mark, - end_mark: parser.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) { - 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) { - 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) { - 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 { - - // 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 - } - } - - // Eat a comment until a line break. - if parser.buffer[parser.buffer_pos] == '#' { - for !is_breakz(parser.buffer, parser.buffer_pos) { - skip(parser) - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - 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] == '#' { - 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] 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] == '#' { - 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 -} diff --git a/vendor/go.yaml.in/yaml/v2/sorter.go b/vendor/go.yaml.in/yaml/v2/sorter.go deleted file mode 100644 index 4c45e660a8..0000000000 --- a/vendor/go.yaml.in/yaml/v2/sorter.go +++ /dev/null @@ -1,113 +0,0 @@ -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()) - for i := 0; i < len(ar) && i < len(br); i++ { - if ar[i] == br[i] { - continue - } - al := unicode.IsLetter(ar[i]) - bl := unicode.IsLetter(br[i]) - if al && bl { - return ar[i] < br[i] - } - if al || bl { - 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") -} diff --git a/vendor/go.yaml.in/yaml/v2/writerc.go b/vendor/go.yaml.in/yaml/v2/writerc.go deleted file mode 100644 index a2dde608cb..0000000000 --- a/vendor/go.yaml.in/yaml/v2/writerc.go +++ /dev/null @@ -1,26 +0,0 @@ -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 -} diff --git a/vendor/go.yaml.in/yaml/v2/yaml.go b/vendor/go.yaml.in/yaml/v2/yaml.go deleted file mode 100644 index 5248e1263c..0000000000 --- a/vendor/go.yaml.in/yaml/v2/yaml.go +++ /dev/null @@ -1,478 +0,0 @@ -// Package yaml implements YAML support for the Go language. -// -// Source code and other details for the project are available at GitHub: -// -// https://github.com/yaml/go-yaml -// -package yaml - -import ( - "errors" - "fmt" - "io" - "reflect" - "strings" - "sync" -) - -// MapSlice encodes and decodes as a YAML map. -// The order of keys is preserved when encoding and decoding. -type MapSlice []MapItem - -// MapItem is an item in a MapSlice. -type MapItem struct { - Key, Value interface{} -} - -// The Unmarshaler interface may be implemented by types to customize their -// behavior when being unmarshaled from a YAML document. The UnmarshalYAML -// method receives a function that may be called to unmarshal the original -// YAML value into a field or variable. It is safe to call the unmarshal -// function parameter more than once if necessary. -type Unmarshaler 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) -} - -// UnmarshalStrict is like Unmarshal except that any fields that are found -// in the data that do not have corresponding struct members, or mapping -// keys that are duplicates, will result in -// an error. -func UnmarshalStrict(in []byte, out interface{}) (err error) { - return unmarshal(in, out, true) -} - -// A Decoder reads and decodes YAML values from an input stream. -type Decoder struct { - strict bool - parser *parser -} - -// 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), - } -} - -// SetStrict sets whether strict decoding behaviour is enabled when -// decoding items in the data (see UnmarshalStrict). By default, decoding is not strict. -func (dec *Decoder) SetStrict(strict bool) { - dec.strict = strict -} - -// 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(dec.strict) - 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 -} - -func unmarshal(in []byte, out interface{}, strict bool) (err error) { - defer handleErr(&err) - d := newDecoder(strict) - 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 -} - -// 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 ")) -} - -// -------------------------------------------------------------------------- -// 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 -} - -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 - -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 - 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: - sinfo, err := getStructInfo(field.Type) - if err != nil { - return nil, err - } - 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 needs a struct value or map field") - return nil, errors.New("Option ,inline needs a struct value 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, - } - - 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 -} - -// FutureLineWrap globally disables line wrapping when encoding long strings. -// This is a temporary and thus deprecated method introduced to faciliate -// migration towards v3, which offers more control of line lengths on -// individual encodings, and has a default matching the behavior introduced -// by this function. -// -// The default formatting of v2 was erroneously changed in v2.3.0 and reverted -// in v2.4.0, at which point this function was introduced to help migration. -func FutureLineWrap() { - disableLineWrapping = true -} diff --git a/vendor/go.yaml.in/yaml/v2/yamlh.go b/vendor/go.yaml.in/yaml/v2/yamlh.go deleted file mode 100644 index f6a9c8e34b..0000000000 --- a/vendor/go.yaml.in/yaml/v2/yamlh.go +++ /dev/null @@ -1,739 +0,0 @@ -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 = iota - - yaml_PLAIN_SCALAR_STYLE // 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. -) - -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", -} - -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 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. - - 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. - - // 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. -} - -// 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_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_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? - - // 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. - } - - // 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. -} diff --git a/vendor/go.yaml.in/yaml/v2/yamlprivateh.go b/vendor/go.yaml.in/yaml/v2/yamlprivateh.go deleted file mode 100644 index 8110ce3c37..0000000000 --- a/vendor/go.yaml.in/yaml/v2/yamlprivateh.go +++ /dev/null @@ -1,173 +0,0 @@ -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 - -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 76e6e2f435..e9fa79c9c2 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -195,16 +195,11 @@ github.com/go-logr/logr/funcr # github.com/go-logr/stdr v1.2.2 ## explicit; go 1.16 github.com/go-logr/stdr -# github.com/gobwas/glob v0.2.3 -## explicit +# github.com/gobwas/glob v1.0.0 +## explicit; go 1.22.0 github.com/gobwas/glob -github.com/gobwas/glob/compiler -github.com/gobwas/glob/match +github.com/gobwas/glob/internal/debug github.com/gobwas/glob/syntax -github.com/gobwas/glob/syntax/ast -github.com/gobwas/glob/syntax/lexer -github.com/gobwas/glob/util/runes -github.com/gobwas/glob/util/strings # github.com/goccy/go-json v0.10.6 ## explicit; go 1.19 github.com/goccy/go-json @@ -260,8 +255,8 @@ github.com/google/go-containerregistry/pkg/v1/types # github.com/google/uuid v1.6.0 ## explicit github.com/google/uuid -# github.com/klauspost/compress v1.19.2 -## explicit; go 1.24 +# github.com/klauspost/compress v1.20.0 +## explicit; go 1.25 github.com/klauspost/compress github.com/klauspost/compress/fse github.com/klauspost/compress/huff0 @@ -276,8 +271,8 @@ github.com/lestrrat-go/backoff/v2 # github.com/lestrrat-go/blackmagic v1.0.4 ## explicit; go 1.23 github.com/lestrrat-go/blackmagic -# github.com/lestrrat-go/dsig v1.2.1 -## explicit; go 1.23.0 +# github.com/lestrrat-go/dsig v1.4.0 +## explicit; go 1.25.0 github.com/lestrrat-go/dsig github.com/lestrrat-go/dsig/internal/ecutil # github.com/lestrrat-go/dsig-secp256k1 v1.0.0 @@ -286,7 +281,7 @@ github.com/lestrrat-go/dsig-secp256k1 # github.com/lestrrat-go/httpcc v1.0.1 ## explicit; go 1.16 github.com/lestrrat-go/httpcc -# github.com/lestrrat-go/httprc/v3 v3.0.5 +# github.com/lestrrat-go/httprc/v3 v3.0.6 ## explicit; go 1.23.0 github.com/lestrrat-go/httprc/v3 github.com/lestrrat-go/httprc/v3/errsink @@ -306,7 +301,7 @@ github.com/lestrrat-go/jwx/internal/pool github.com/lestrrat-go/jwx/jwa github.com/lestrrat-go/jwx/jwk github.com/lestrrat-go/jwx/x25519 -# github.com/lestrrat-go/jwx/v3 v3.1.1 +# github.com/lestrrat-go/jwx/v3 v3.3.0 ## explicit; go 1.25.0 github.com/lestrrat-go/jwx/v3 github.com/lestrrat-go/jwx/v3/cert @@ -329,6 +324,8 @@ github.com/lestrrat-go/jwx/v3/jwk/ecdsa github.com/lestrrat-go/jwx/v3/jwk/internal/registry github.com/lestrrat-go/jwx/v3/jwk/jwkbb github.com/lestrrat-go/jwx/v3/jws +github.com/lestrrat-go/jwx/v3/jws/internal/jwsbb +github.com/lestrrat-go/jwx/v3/jws/internal/keyalg github.com/lestrrat-go/jwx/v3/jws/internal/keytype github.com/lestrrat-go/jwx/v3/jws/jwsbb github.com/lestrrat-go/jwx/v3/jws/legacy @@ -369,7 +366,7 @@ github.com/moby/sys/userns # github.com/mrunalp/fileutils v0.5.1 ## explicit; go 1.13 github.com/mrunalp/fileutils -# github.com/open-policy-agent/opa v1.20.1 +# github.com/open-policy-agent/opa v1.21.1 ## explicit; go 1.26.0 github.com/open-policy-agent/opa/capabilities github.com/open-policy-agent/opa/internal/bundle @@ -409,6 +406,7 @@ github.com/open-policy-agent/opa/internal/wasm/opcode github.com/open-policy-agent/opa/internal/wasm/sdk/opa/capabilities github.com/open-policy-agent/opa/internal/wasm/types github.com/open-policy-agent/opa/internal/wasm/util +github.com/open-policy-agent/opa/internal/yaml github.com/open-policy-agent/opa/v1/ast github.com/open-policy-agent/opa/v1/ast/internal/scanner github.com/open-policy-agent/opa/v1/ast/internal/tokens @@ -523,7 +521,7 @@ github.com/segmentio/asm/cpu/arm64 github.com/segmentio/asm/cpu/cpuid github.com/segmentio/asm/cpu/x86 github.com/segmentio/asm/internal/unsafebytes -# github.com/sirupsen/logrus v1.10.0 +# github.com/sirupsen/logrus v1.10.2 ## explicit; go 1.23 github.com/sirupsen/logrus # github.com/tchap/go-patricia/v2 v2.3.3 @@ -539,7 +537,7 @@ github.com/urfave/cli/v2 ## explicit; go 1.24 github.com/valyala/fastjson github.com/valyala/fastjson/fastfloat -# github.com/vektah/gqlparser/v2 v2.5.36 +# github.com/vektah/gqlparser/v2 v2.5.37 ## explicit; go 1.22 github.com/vektah/gqlparser/v2/ast github.com/vektah/gqlparser/v2/gqlerror @@ -632,9 +630,6 @@ go.opentelemetry.io/otel/trace/noop go.uber.org/mock/gomock go.uber.org/mock/mockgen go.uber.org/mock/mockgen/model -# go.yaml.in/yaml/v2 v2.4.4 -## explicit; go 1.15 -go.yaml.in/yaml/v2 # go.yaml.in/yaml/v3 v3.0.5 ## explicit; go 1.16 go.yaml.in/yaml/v3 @@ -831,7 +826,4 @@ google.golang.org/protobuf/types/known/fieldmaskpb google.golang.org/protobuf/types/known/structpb google.golang.org/protobuf/types/known/timestamppb google.golang.org/protobuf/types/pluginpb -# sigs.k8s.io/yaml v1.6.0 -## explicit; go 1.22 -sigs.k8s.io/yaml # google.golang.org/genproto => google.golang.org/genproto v0.0.0-20250428153025-10db94c68c34 diff --git a/vendor/sigs.k8s.io/yaml/.gitignore b/vendor/sigs.k8s.io/yaml/.gitignore deleted file mode 100644 index 2dc92904ef..0000000000 --- a/vendor/sigs.k8s.io/yaml/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# OSX leaves these everywhere on SMB shares -._* - -# Eclipse files -.classpath -.project -.settings/** - -# Idea files -.idea/** -.idea/ - -# Emacs save files -*~ - -# Vim-related files -[._]*.s[a-w][a-z] -[._]s[a-w][a-z] -*.un~ -Session.vim -.netrwhist - -# Go test binaries -*.test diff --git a/vendor/sigs.k8s.io/yaml/CONTRIBUTING.md b/vendor/sigs.k8s.io/yaml/CONTRIBUTING.md deleted file mode 100644 index de47115137..0000000000 --- a/vendor/sigs.k8s.io/yaml/CONTRIBUTING.md +++ /dev/null @@ -1,31 +0,0 @@ -# Contributing Guidelines - -Welcome to Kubernetes. We are excited about the prospect of you joining our [community](https://github.com/kubernetes/community)! The Kubernetes community abides by the CNCF [code of conduct](code-of-conduct.md). Here is an excerpt: - -_As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities._ - -## Getting Started - -We have full documentation on how to get started contributing here: - - - -- [Contributor License Agreement](https://git.k8s.io/community/CLA.md) Kubernetes projects require that you sign a Contributor License Agreement (CLA) before we can accept your pull requests -- [Kubernetes Contributor Guide](http://git.k8s.io/community/contributors/guide) - Main contributor documentation, or you can just jump directly to the [contributing section](http://git.k8s.io/community/contributors/guide#contributing) -- [Contributor Cheat Sheet](https://git.k8s.io/community/contributors/guide/contributor-cheatsheet.md) - Common resources for existing developers - -## Mentorship - -- [Mentoring Initiatives](https://git.k8s.io/community/mentoring) - We have a diverse set of mentorship programs available that are always looking for volunteers! - - diff --git a/vendor/sigs.k8s.io/yaml/LICENSE b/vendor/sigs.k8s.io/yaml/LICENSE deleted file mode 100644 index 093d6d3edf..0000000000 --- a/vendor/sigs.k8s.io/yaml/LICENSE +++ /dev/null @@ -1,306 +0,0 @@ -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. - -# The forked go-yaml.v3 library under 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. - -# The forked go-yaml.v2 library under the project is covered by an -Apache 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. diff --git a/vendor/sigs.k8s.io/yaml/OWNERS b/vendor/sigs.k8s.io/yaml/OWNERS deleted file mode 100644 index 003a149e15..0000000000 --- a/vendor/sigs.k8s.io/yaml/OWNERS +++ /dev/null @@ -1,23 +0,0 @@ -# See the OWNERS docs at https://go.k8s.io/owners - -approvers: -- dims -- jpbetz -- smarterclayton -- deads2k -- sttts -- liggitt -reviewers: -- dims -- thockin -- jpbetz -- smarterclayton -- wojtek-t -- deads2k -- derekwaynecarr -- mikedanese -- liggitt -- sttts -- tallclair -labels: -- sig/api-machinery diff --git a/vendor/sigs.k8s.io/yaml/README.md b/vendor/sigs.k8s.io/yaml/README.md deleted file mode 100644 index e81cc426be..0000000000 --- a/vendor/sigs.k8s.io/yaml/README.md +++ /dev/null @@ -1,123 +0,0 @@ -# YAML marshaling and unmarshaling support for Go - -[![Build Status](https://travis-ci.org/kubernetes-sigs/yaml.svg)](https://travis-ci.org/kubernetes-sigs/yaml) - -kubernetes-sigs/yaml is a permanent fork of [ghodss/yaml](https://github.com/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://web.archive.org/web/20190603050330/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 sigs.k8s.io/yaml -``` - -And import using: - -``` -import "sigs.k8s.io/yaml" -``` - -Usage is very similar to the JSON library: - -```go -package main - -import ( - "fmt" - - "sigs.k8s.io/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" - - "sigs.k8s.io/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: - age: 30 - name: John - */ - j2, err := yaml.YAMLToJSON(y) - if err != nil { - fmt.Printf("err: %v\n", err) - return - } - fmt.Println(string(j2)) - /* Output: - {"age":30,"name":"John"} - */ -} -``` diff --git a/vendor/sigs.k8s.io/yaml/RELEASE.md b/vendor/sigs.k8s.io/yaml/RELEASE.md deleted file mode 100644 index 6b642464e5..0000000000 --- a/vendor/sigs.k8s.io/yaml/RELEASE.md +++ /dev/null @@ -1,9 +0,0 @@ -# Release Process - -The `yaml` Project is released on an as-needed basis. The process is as follows: - -1. An issue is proposing a new release with a changelog since the last release -1. All [OWNERS](OWNERS) must LGTM this release -1. An OWNER runs `git tag -s $VERSION` and inserts the changelog and pushes the tag with `git push $VERSION` -1. The release issue is closed -1. An announcement email is sent to `kubernetes-dev@googlegroups.com` with the subject `[ANNOUNCE] kubernetes-template-project $VERSION is released` diff --git a/vendor/sigs.k8s.io/yaml/SECURITY_CONTACTS b/vendor/sigs.k8s.io/yaml/SECURITY_CONTACTS deleted file mode 100644 index 0648a8ebff..0000000000 --- a/vendor/sigs.k8s.io/yaml/SECURITY_CONTACTS +++ /dev/null @@ -1,17 +0,0 @@ -# Defined below are the security contacts for this repo. -# -# They are the contact point for the Product Security Team to reach out -# to for triaging and handling of incoming issues. -# -# The below names agree to abide by the -# [Embargo Policy](https://github.com/kubernetes/sig-release/blob/master/security-release-process-documentation/security-release-process.md#embargo-policy) -# and will be removed and replaced if they violate that agreement. -# -# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE -# INSTRUCTIONS AT https://kubernetes.io/security/ - -cjcullen -jessfraz -liggitt -philips -tallclair diff --git a/vendor/sigs.k8s.io/yaml/code-of-conduct.md b/vendor/sigs.k8s.io/yaml/code-of-conduct.md deleted file mode 100644 index 0d15c00cf3..0000000000 --- a/vendor/sigs.k8s.io/yaml/code-of-conduct.md +++ /dev/null @@ -1,3 +0,0 @@ -# Kubernetes Community Code of Conduct - -Please refer to our [Kubernetes Community Code of Conduct](https://git.k8s.io/community/code-of-conduct.md) diff --git a/vendor/sigs.k8s.io/yaml/fields.go b/vendor/sigs.k8s.io/yaml/fields.go deleted file mode 100644 index 0ea28bd030..0000000000 --- a/vendor/sigs.k8s.io/yaml/fields.go +++ /dev/null @@ -1,501 +0,0 @@ -// 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 'value' 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(value reflect.Value, decodingNull bool) (json.Unmarshaler, encoding.TextUnmarshaler, reflect.Value) { - // If 'value' is a named type and is addressable, - // start with its address, so that if the type has pointer methods, - // we find them. - if value.Kind() != reflect.Ptr && value.Type().Name() != "" && value.CanAddr() { - value = value.Addr() - } - for { - // Load value from interface, but only if the result will be - // usefully addressable. - if value.Kind() == reflect.Interface && !value.IsNil() { - element := value.Elem() - if element.Kind() == reflect.Ptr && !element.IsNil() && (!decodingNull || element.Elem().Kind() == reflect.Ptr) { - value = element - continue - } - } - - if value.Kind() != reflect.Ptr { - break - } - - if value.Elem().Kind() != reflect.Ptr && decodingNull && value.CanSet() { - break - } - if value.IsNil() { - if value.CanSet() { - value.Set(reflect.New(value.Type().Elem())) - } else { - value = reflect.New(value.Type().Elem()) - } - } - if value.Type().NumMethod() > 0 { - if u, ok := value.Interface().(json.Unmarshaler); ok { - return u, nil, reflect.Value{} - } - if u, ok := value.Interface().(encoding.TextUnmarshaler); ok { - return nil, u, reflect.Value{} - } - } - value = value.Elem() - } - return nil, nil, value -} - -// 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 map[reflect.Type]int - var 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 -} diff --git a/vendor/sigs.k8s.io/yaml/yaml.go b/vendor/sigs.k8s.io/yaml/yaml.go deleted file mode 100644 index aa01acd45d..0000000000 --- a/vendor/sigs.k8s.io/yaml/yaml.go +++ /dev/null @@ -1,426 +0,0 @@ -/* -Copyright 2021 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 yaml - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "reflect" - "strconv" - - "go.yaml.in/yaml/v2" -) - -// Marshal marshals obj into JSON using stdlib json.Marshal, and then converts JSON to YAML using JSONToYAML (see that method for more reference) -func Marshal(obj interface{}) ([]byte, error) { - jsonBytes, err := json.Marshal(obj) - if err != nil { - return nil, fmt.Errorf("error marshaling into JSON: %w", err) - } - - return JSONToYAML(jsonBytes) -} - -// JSONOpt is a decoding option for decoding from JSON format. -type JSONOpt func(*json.Decoder) *json.Decoder - -// Unmarshal first converts the given YAML to JSON, and then unmarshals the JSON into obj. Options for the -// standard library json.Decoder can be optionally specified, e.g. to decode untyped numbers into json.Number instead of float64, or to disallow unknown fields (but for that purpose, see also UnmarshalStrict). obj must be a non-nil pointer. -// -// Important notes about the Unmarshal logic: -// -// - Decoding is case-insensitive, unlike the rest of Kubernetes API machinery, as this is using the stdlib json library. This might be confusing to users. -// - This decodes any number (although it is an integer) into a float64 if the type of obj is unknown, e.g. *map[string]interface{}, *interface{}, or *[]interface{}. This means integers above +/- 2^53 will lose precision when round-tripping. Make a JSONOpt that calls d.UseNumber() to avoid this. -// - Duplicate fields, including in-case-sensitive matches, are ignored in an undefined order. Note that the YAML specification forbids duplicate fields, so this logic is more permissive than it needs to. See UnmarshalStrict for an alternative. -// - Unknown fields, i.e. serialized data that do not map to a field in obj, are ignored. Use d.DisallowUnknownFields() or UnmarshalStrict to override. -// - As per the YAML 1.1 specification, which yaml.v2 used underneath implements, literal 'yes' and 'no' strings without quotation marks will be converted to true/false implicitly. -// - YAML non-string keys, e.g. ints, bools and floats, are converted to strings implicitly during the YAML to JSON conversion process. -// - There are no compatibility guarantees for returned error values. -func Unmarshal(yamlBytes []byte, obj interface{}, opts ...JSONOpt) error { - return unmarshal(yamlBytes, obj, yaml.Unmarshal, opts...) -} - -// UnmarshalStrict is similar to Unmarshal (please read its documentation for reference), with the following exceptions: -// -// - Duplicate fields in an object yield an error. This is according to the YAML specification. -// - If obj, or any of its recursive children, is a struct, presence of fields in the serialized data unknown to the struct will yield an error. -func UnmarshalStrict(yamlBytes []byte, obj interface{}, opts ...JSONOpt) error { - return unmarshal(yamlBytes, obj, yaml.UnmarshalStrict, append(opts, DisallowUnknownFields)...) -} - -// unmarshal unmarshals the given YAML byte stream into the given interface, -// optionally performing the unmarshalling strictly -func unmarshal(yamlBytes []byte, obj interface{}, unmarshalFn func([]byte, interface{}) error, opts ...JSONOpt) error { - jsonTarget := reflect.ValueOf(obj) - - jsonBytes, err := yamlToJSONTarget(yamlBytes, &jsonTarget, unmarshalFn) - if err != nil { - return fmt.Errorf("error converting YAML to JSON: %w", err) - } - - err = jsonUnmarshal(bytes.NewReader(jsonBytes), obj, opts...) - if err != nil { - return fmt.Errorf("error unmarshaling JSON: %w", 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(reader io.Reader, obj interface{}, opts ...JSONOpt) error { - d := json.NewDecoder(reader) - for _, opt := range opts { - d = opt(d) - } - if err := d.Decode(&obj); err != nil { - return fmt.Errorf("while decoding JSON: %w", err) - } - return nil -} - -// JSONToYAML converts JSON to YAML. Notable implementation details: -// -// - Duplicate fields, are case-sensitively ignored in an undefined order. -// - The sequence indentation style is compact, which means that the "- " marker for a YAML sequence will be on the same indentation level as the sequence field name. -// - Unlike Unmarshal, all integers, up to 64 bits, are preserved during this round-trip. -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. - yamlBytes, err := yaml.Marshal(jsonObj) - if err != nil { - return nil, err - } - - return yamlBytes, nil -} - -// YAMLToJSON converts YAML to JSON. Since JSON is a subset of YAML, -// passing JSON through this method should be a no-op. -// -// Some 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, and therefore int, bool and float keys are converted to strings implicitly. -// - 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. -// - And more... read the YAML specification for more details. -// -// Notable about the implementation: -// -// - Duplicate fields are case-sensitively ignored in an undefined order. Note that the YAML specification forbids duplicate fields, so this logic is more permissive than it needs to. See YAMLToJSONStrict for an alternative. -// - As per the YAML 1.1 specification, which yaml.v2 used underneath implements, literal 'yes' and 'no' strings without quotation marks will be converted to true/false implicitly. -// - Unlike Unmarshal, all integers, up to 64 bits, are preserved during this round-trip. -// - There are no compatibility guarantees for returned error values. -func YAMLToJSON(y []byte) ([]byte, error) { - return yamlToJSONTarget(y, nil, yaml.Unmarshal) -} - -// YAMLToJSONStrict is like YAMLToJSON but enables strict YAML decoding, -// returning an error on any duplicate field names. -func YAMLToJSONStrict(y []byte) ([]byte, error) { - return yamlToJSONTarget(y, nil, yaml.UnmarshalStrict) -} - -func yamlToJSONTarget(yamlBytes []byte, jsonTarget *reflect.Value, unmarshalFn func([]byte, interface{}) error) ([]byte, error) { - // Convert the YAML to an object. - var yamlObj interface{} - err := unmarshalFn(yamlBytes, &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. - jsonBytes, err := json.Marshal(jsonObj) - if err != nil { - return nil, err - } - return jsonBytes, nil -} - -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 { - jsonUnmarshaler, textUnmarshaler, pointerValue := 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 jsonUnmarshaler != nil || textUnmarshaler != nil { - jsonTarget = nil - } else { - jsonTarget = &pointerValue - } - } - - // 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 - } -} - -// JSONObjectToYAMLObject converts an in-memory JSON object into a YAML in-memory MapSlice, -// without going through a byte representation. A nil or empty map[string]interface{} input is -// converted to an empty map, i.e. yaml.MapSlice(nil). -// -// interface{} slices stay interface{} slices. map[string]interface{} becomes yaml.MapSlice. -// -// int64 and float64 are down casted following the logic of github.com/go-yaml/yaml: -// - float64s are down-casted as far as possible without data-loss to int, int64, uint64. -// - int64s are down-casted to int if possible without data-loss. -// -// Big int/int64/uint64 do not lose precision as in the json-yaml roundtripping case. -// -// string, bool and any other types are unchanged. -func JSONObjectToYAMLObject(j map[string]interface{}) yaml.MapSlice { - if len(j) == 0 { - return nil - } - ret := make(yaml.MapSlice, 0, len(j)) - for k, v := range j { - ret = append(ret, yaml.MapItem{Key: k, Value: jsonToYAMLValue(v)}) - } - return ret -} - -func jsonToYAMLValue(j interface{}) interface{} { - switch j := j.(type) { - case map[string]interface{}: - if j == nil { - return interface{}(nil) - } - return JSONObjectToYAMLObject(j) - case []interface{}: - if j == nil { - return interface{}(nil) - } - ret := make([]interface{}, len(j)) - for i := range j { - ret[i] = jsonToYAMLValue(j[i]) - } - return ret - case float64: - // replicate the logic in https://github.com/go-yaml/yaml/blob/51d6538a90f86fe93ac480b35f37b2be17fef232/resolve.go#L151 - if i64 := int64(j); j == float64(i64) { - if i := int(i64); i64 == int64(i) { - return i - } - return i64 - } - if ui64 := uint64(j); j == float64(ui64) { - return ui64 - } - return j - case int64: - if i := int(j); j == int64(i) { - return i - } - return j - } - return j -} - -// DisallowUnknownFields configures the JSON decoder to error out if unknown -// fields come along, instead of dropping them by default. -func DisallowUnknownFields(d *json.Decoder) *json.Decoder { - d.DisallowUnknownFields() - return d -}