chore: bump reva 81e6e21 (#11922)

This commit is contained in:
Michal Klos
2026-01-19 11:04:22 +01:00
committed by GitHub
parent 0cdb4f3f6d
commit 0bf8179b66
418 changed files with 24339 additions and 18802 deletions

93
go.mod
View File

@@ -7,7 +7,7 @@ toolchain go1.24.9
require (
dario.cat/mergo v1.0.2
github.com/CiscoM31/godata v1.0.10
github.com/KimMachineGun/automemlimit v0.7.4
github.com/KimMachineGun/automemlimit v0.7.5
github.com/Masterminds/semver v1.5.0
github.com/MicahParks/keyfunc/v2 v2.1.0
github.com/Nerzal/gocloak/v13 v13.9.0
@@ -15,13 +15,13 @@ require (
github.com/beevik/etree v1.6.0
github.com/blevesearch/bleve/v2 v2.5.7
github.com/cenkalti/backoff v2.2.1+incompatible
github.com/coreos/go-oidc/v3 v3.16.0
github.com/coreos/go-oidc/v3 v3.17.0
github.com/cs3org/go-cs3apis v0.0.0-20241105092511-3ad35d174fc1
github.com/davidbyttow/govips/v2 v2.16.0
github.com/dhowden/tag v0.0.0-20240417053706-3d75831295e8
github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e
github.com/egirna/icap-client v0.1.1
github.com/gabriel-vasile/mimetype v1.4.10
github.com/gabriel-vasile/mimetype v1.4.12
github.com/ggwhite/go-masker v1.1.0
github.com/go-chi/chi/v5 v5.2.4
github.com/go-chi/render v1.0.3
@@ -35,7 +35,7 @@ require (
github.com/go-micro/plugins/v4/store/nats-js-kv v0.0.0-20240726082623-6831adfdcdc4
github.com/go-micro/plugins/v4/wrapper/monitoring/prometheus v1.2.0
github.com/go-micro/plugins/v4/wrapper/trace/opentelemetry v1.2.0
github.com/go-playground/validator/v10 v10.28.0
github.com/go-playground/validator/v10 v10.30.1
github.com/gofrs/uuid v4.4.0+incompatible
github.com/golang-jwt/jwt/v5 v5.3.0
github.com/golang/protobuf v1.5.4
@@ -57,16 +57,16 @@ require (
github.com/mitchellh/mapstructure v1.5.0
github.com/mna/pigeon v1.3.0
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
github.com/nats-io/nats-server/v2 v2.12.1
github.com/nats-io/nats.go v1.46.1
github.com/nats-io/nats-server/v2 v2.12.3
github.com/nats-io/nats.go v1.48.0
github.com/olekukonko/tablewriter v1.1.0
github.com/onsi/ginkgo v1.16.5
github.com/onsi/ginkgo/v2 v2.27.2
github.com/onsi/gomega v1.38.2
github.com/onsi/ginkgo/v2 v2.27.4
github.com/onsi/gomega v1.38.3
github.com/open-policy-agent/opa v1.10.1
github.com/orcaman/concurrent-map v1.0.0
github.com/owncloud/libre-graph-api-go v1.0.5-0.20251107084958-31937a4ea3f1
github.com/owncloud/reva/v2 v2.0.0-20251107154850-a122a9538794
github.com/owncloud/reva/v2 v2.0.0-20260116122933-81e6e21256eb
github.com/pkg/errors v0.9.1
github.com/pkg/xattr v0.4.12
github.com/prometheus/client_golang v1.23.2
@@ -74,7 +74,7 @@ require (
github.com/rogpeppe/go-internal v1.14.1
github.com/rs/cors v1.11.1
github.com/rs/zerolog v1.34.0
github.com/shamaton/msgpack/v2 v2.3.1
github.com/shamaton/msgpack/v2 v2.4.0
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af
github.com/spf13/afero v1.15.0
github.com/spf13/cobra v1.10.1
@@ -88,25 +88,25 @@ require (
github.com/xhit/go-simple-mail/v2 v2.16.0
go-micro.dev/v4 v4.11.0
go.etcd.io/bbolt v1.4.2
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0
go.opentelemetry.io/contrib/zpages v0.63.0
go.opentelemetry.io/otel v1.38.0
go.opentelemetry.io/otel v1.39.0
go.opentelemetry.io/otel/exporters/jaeger v1.17.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0
go.opentelemetry.io/otel/sdk v1.38.0
go.opentelemetry.io/otel/trace v1.38.0
golang.org/x/crypto v0.45.0
go.opentelemetry.io/otel/sdk v1.39.0
go.opentelemetry.io/otel/trace v1.39.0
golang.org/x/crypto v0.46.0
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b
golang.org/x/image v0.32.0
golang.org/x/net v0.47.0
golang.org/x/oauth2 v0.34.0
golang.org/x/sync v0.18.0
golang.org/x/sync v0.19.0
golang.org/x/term v0.39.0
golang.org/x/text v0.31.0
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5
google.golang.org/grpc v1.76.0
google.golang.org/protobuf v1.36.10
golang.org/x/text v0.32.0
google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8
google.golang.org/grpc v1.77.0
google.golang.org/protobuf v1.36.11
gopkg.in/yaml.v2 v2.4.0
gotest.tools/v3 v3.5.2
stash.kopano.io/kgol/rndm v1.1.2
@@ -116,7 +116,7 @@ require (
contrib.go.opencensus.io/exporter/prometheus v0.4.2 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
github.com/BurntSushi/toml v1.5.0 // indirect
github.com/BurntSushi/toml v1.6.0 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.4.0 // indirect
github.com/Masterminds/sprig v2.22.0+incompatible // indirect
@@ -127,7 +127,7 @@ require (
github.com/ajg/form v1.5.1 // indirect
github.com/alexedwards/argon2id v1.0.0 // indirect
github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964 // indirect
github.com/antithesishq/antithesis-sdk-go v0.4.3-default-no-op // indirect
github.com/antithesishq/antithesis-sdk-go v0.5.0-default-no-op // indirect
github.com/armon/go-radix v1.0.0 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/aws/aws-sdk-go v1.55.8 // indirect
@@ -154,12 +154,12 @@ require (
github.com/bluele/gcache v0.0.2 // indirect
github.com/bombsimon/logrusr/v3 v3.1.0 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/ceph/go-ceph v0.36.0 // indirect
github.com/ceph/go-ceph v0.37.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cevaris/ordered_map v0.0.0-20190319150403-3adeae072e73 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
github.com/coreos/go-semver v0.3.1 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/coreos/go-systemd/v22 v22.6.0 // indirect
github.com/cornelk/hashmap v1.0.8 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
github.com/crewjam/httperr v0.2.0 // indirect
@@ -212,16 +212,16 @@ require (
github.com/gobwas/ws v1.2.1 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/goccy/go-yaml v1.18.0 // indirect
github.com/gofrs/flock v0.12.1 // indirect
github.com/gofrs/flock v0.13.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/gomodule/redigo v1.9.2 // indirect
github.com/gomodule/redigo v1.9.3 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/go-tpm v0.9.6 // indirect
github.com/google/go-tpm v0.9.7 // indirect
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect
github.com/google/renameio/v2 v2.0.0 // indirect
github.com/google/renameio/v2 v2.0.1 // indirect
github.com/gookit/goutil v0.7.1 // indirect
github.com/gorilla/handlers v1.5.1 // indirect
github.com/gorilla/schema v1.4.1 // indirect
@@ -240,8 +240,9 @@ require (
github.com/juliangruber/go-intersect v1.1.0 // indirect
github.com/kettek/apng v0.0.0-20250827064933-2bb5f5fcf253 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/klauspost/compress v1.18.2 // indirect
github.com/klauspost/cpuid/v2 v2.2.11 // indirect
github.com/klauspost/crc32 v1.3.0 // indirect
github.com/kovidgoyal/go-parallel v1.1.1 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/lestrrat-go/blackmagic v1.0.4 // indirect
@@ -259,15 +260,15 @@ require (
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mattn/go-sqlite3 v1.14.32 // indirect
github.com/mattn/go-sqlite3 v1.14.33 // indirect
github.com/maxymania/go-system v0.0.0-20170110133659-647cc364bf0b // indirect
github.com/mendsley/gojwk v0.0.0-20141217222730-4d5ec6e58103 // indirect
github.com/miekg/dns v1.1.67 // indirect
github.com/mileusna/useragent v1.3.5 // indirect
github.com/minio/crc64nvme v1.0.2 // indirect
github.com/minio/highwayhash v1.0.3 // indirect
github.com/minio/crc64nvme v1.1.0 // indirect
github.com/minio/highwayhash v1.0.4-0.20251030100505-070ab1a87a76 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/minio/minio-go/v7 v7.0.95 // indirect
github.com/minio/minio-go/v7 v7.0.97 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
@@ -275,10 +276,10 @@ require (
github.com/mschoch/smat v0.2.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nats-io/jwt/v2 v2.8.0 // indirect
github.com/nats-io/nkeys v0.4.11 // indirect
github.com/nats-io/nkeys v0.4.12 // indirect
github.com/nats-io/nuid v1.0.1 // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/oklog/run v1.1.0 // indirect
github.com/oklog/run v1.2.0 // indirect
github.com/olekukonko/errors v1.1.0 // indirect
github.com/olekukonko/ll v0.0.9 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
@@ -291,9 +292,9 @@ require (
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/pquerna/cachecontrol v0.2.0 // indirect
github.com/prometheus/alertmanager v0.28.1 // indirect
github.com/prometheus/alertmanager v0.30.0 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.66.1 // indirect
github.com/prometheus/common v0.67.4 // indirect
github.com/prometheus/procfs v0.17.0 // indirect
github.com/prometheus/statsd_exporter v0.22.8 // indirect
github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 // indirect
@@ -329,24 +330,24 @@ require (
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
github.com/yashtewari/glob-intersection v0.2.0 // indirect
go.etcd.io/etcd/api/v3 v3.6.5 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.6.5 // indirect
go.etcd.io/etcd/client/v3 v3.6.5 // indirect
go.etcd.io/etcd/api/v3 v3.6.7 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.6.7 // indirect
go.etcd.io/etcd/client/v3 v3.6.7 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
go.opentelemetry.io/otel/metric v1.38.0 // indirect
go.opentelemetry.io/otel/metric v1.39.0 // indirect
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/mod v0.29.0 // indirect
golang.org/x/mod v0.30.0 // indirect
golang.org/x/sys v0.40.0 // indirect
golang.org/x/time v0.14.0 // indirect
golang.org/x/tools v0.38.0 // indirect
golang.org/x/tools v0.39.0 // indirect
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect

193
go.sum
View File

@@ -44,15 +44,15 @@ github.com/Acconut/go-httptest-recorder v1.0.0/go.mod h1:CwQyhTH1kq/gLyWiRieo7c0
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/CiscoM31/godata v1.0.10 h1:DZdJ6M8QNh4HquvDDOqNLu6h77Wl86KGK7Qlbmb90sk=
github.com/CiscoM31/godata v1.0.10/go.mod h1:ZMiT6JuD3Rm83HEtiTx4JEChsd25YCrxchKGag/sdTc=
github.com/DeepDiver1975/secure v0.0.0-20240611112133-abc838fb797c h1:ocsNvQ2tNHme4v/lTs17HROamc7mFzZfzWcg4m+UXN0=
github.com/DeepDiver1975/secure v0.0.0-20240611112133-abc838fb797c/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40=
github.com/KimMachineGun/automemlimit v0.7.4 h1:UY7QYOIfrr3wjjOAqahFmC3IaQCLWvur9nmfIn6LnWk=
github.com/KimMachineGun/automemlimit v0.7.4/go.mod h1:QZxpHaGOQoYvFhv/r4u3U0JTC2ZcOwbSr11UZF46UBM=
github.com/KimMachineGun/automemlimit v0.7.5 h1:RkbaC0MwhjL1ZuBKunGDjE/ggwAX43DwZrJqVwyveTk=
github.com/KimMachineGun/automemlimit v0.7.5/go.mod h1:QZxpHaGOQoYvFhv/r4u3U0JTC2ZcOwbSr11UZF46UBM=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
@@ -92,8 +92,8 @@ github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNg
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/antithesishq/antithesis-sdk-go v0.4.3-default-no-op h1:+OSa/t11TFhqfrX0EOSqQBDJ0YlpmK0rDSiB19dg9M0=
github.com/antithesishq/antithesis-sdk-go v0.4.3-default-no-op/go.mod h1:IUpT2DPAKh6i/YhSbt6Gl3v2yvUZjmKncl7U91fup7E=
github.com/antithesishq/antithesis-sdk-go v0.5.0-default-no-op h1:Ucf+QxEKMbPogRO5guBNe5cgd9uZgfoJLOYs8WWhtjM=
github.com/antithesishq/antithesis-sdk-go v0.5.0-default-no-op/go.mod h1:IUpT2DPAKh6i/YhSbt6Gl3v2yvUZjmKncl7U91fup7E=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
@@ -170,8 +170,8 @@ github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QH
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/ceph/go-ceph v0.36.0 h1:IDE4vEF+4fmjve+CPjD1WStgfQ+Lh6vD+9PMUI712KI=
github.com/ceph/go-ceph v0.36.0/go.mod h1:fGCbndVDLuHW7q2954d6y+tgPFOBnRLqJRe2YXyngw4=
github.com/ceph/go-ceph v0.37.0 h1:KXliBe3ZDr3/AtfY7n9d1MG7ippYNCVhMPcAgm05CFI=
github.com/ceph/go-ceph v0.37.0/go.mod h1:3y2tOlITlyuVFhy8v6PpCEfjMwKPfXJiH0/2hKZZQRE=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
@@ -185,12 +185,13 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/coreos/go-oidc/v3 v3.16.0 h1:qRQUCFstKpXwmEjDQTIbyY/5jF00+asXzSkmkoa/mow=
github.com/coreos/go-oidc/v3 v3.16.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8=
github.com/coreos/go-oidc/v3 v3.17.0 h1:hWBGaQfbi0iVviX4ibC7bk8OKT5qNr4klBaCHVNvehc=
github.com/coreos/go-oidc/v3 v3.17.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8=
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/go-systemd/v22 v22.6.0 h1:aGVa/v8B7hpb0TKl0MWoAavPDmHvobFe5R5zn0bCJWo=
github.com/coreos/go-systemd/v22 v22.6.0/go.mod h1:iG+pp635Fo7ZmV/j14KUcmEyWF+0X7Lua8rrTWzYgWU=
github.com/cornelk/hashmap v1.0.8 h1:nv0AWgw02n+iDcawr5It4CjQIAcdMMKRrs10HOJYlrc=
github.com/cornelk/hashmap v1.0.8/go.mod h1:RfZb7JO3RviW/rT6emczVuC/oxpdz4UsSB2LJSclR1k=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
@@ -265,8 +266,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0=
github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=
github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/gdexlab/go-render v1.0.1 h1:rxqB3vo5s4n1kF0ySmoNeSPRYkEsyHgln4jFIQY7v0U=
github.com/gdexlab/go-render v1.0.1/go.mod h1:wRi5nW2qfjiGj4mPukH4UV0IknS1cHD4VgFTmJX5JzM=
github.com/ggwhite/go-masker v1.1.0 h1:kN/KIvktu2U+hd3KWrSlLj7xBGD1iBfc9/xdbVgFbRc=
@@ -358,8 +359,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688=
github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU=
github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w=
github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM=
@@ -389,12 +390,12 @@ github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PU
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw=
github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0=
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid/v5 v5.3.2 h1:2jfO8j3XgSwlz/wHqemAEugfnTlikAYHhnqQ8Xh4fE0=
github.com/gofrs/uuid/v5 v5.3.2/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/gofrs/uuid/v5 v5.4.0 h1:EfbpCTjqMuGyq5ZJwxqzn3Cbr2d0rUZU7v5ycAk/e/0=
github.com/gofrs/uuid/v5 v5.4.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
@@ -438,8 +439,8 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomodule/redigo v1.9.2 h1:HrutZBLhSIU8abiSfW8pj8mPhOyMYjZT/wcA4/L9L9s=
github.com/gomodule/redigo v1.9.2/go.mod h1:KsU3hiK/Ay8U42qpaJk+kuNa3C+spxapWpM+ywhcgtw=
github.com/gomodule/redigo v1.9.3 h1:dNPSXeXv6HCq2jdyWfjgmhBdqnR6PRO3m/G05nvpPC8=
github.com/gomodule/redigo v1.9.3/go.mod h1:KsU3hiK/Ay8U42qpaJk+kuNa3C+spxapWpM+ywhcgtw=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q=
@@ -464,8 +465,8 @@ github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/go-tika v0.3.1 h1:l+jr10hDhZjcgxFRfcQChRLo1bPXQeLFluMyvDhXTTA=
github.com/google/go-tika v0.3.1/go.mod h1:DJh5N8qxXIl85QkqmXknd+PeeRkUOTbvwyYf7ieDz6c=
github.com/google/go-tpm v0.9.6 h1:Ku42PT4LmjDu1H5C5ISWLlpI1mj+Zq7sPGKoRw2XROA=
github.com/google/go-tpm v0.9.6/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
github.com/google/go-tpm v0.9.7 h1:u89J4tUUeDTlH8xxC3CTW7OHZjbjKoHdQ9W7gCUhtxA=
github.com/google/go-tpm v0.9.7/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@@ -479,8 +480,8 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg=
github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4=
github.com/google/renameio/v2 v2.0.1 h1:HyOM6qd9gF9sf15AvhbptGHUnaLTpEI9akAFFU3VyW0=
github.com/google/renameio/v2 v2.0.1/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -575,11 +576,13 @@ github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.11 h1:0OwqZRYI2rFrjS4kvkDnqJkKHdHaRnCm68/DY4OxRzU=
github.com/klauspost/cpuid/v2 v2.2.11/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/klauspost/crc32 v1.3.0 h1:sSmTt3gUt81RP655XGZPElI0PelVTZ6YwCRnPSupoFM=
github.com/klauspost/crc32 v1.3.0/go.mod h1:D7kQaZhnkX/Y0tstFGf8VUzv2UofNGqCjnC3zdHB0Hw=
github.com/kobergj/go-micro/v4 v4.0.0-20250610135441-d0b187215699 h1:3TOdtI6WPyvBB+uCykapjRtQX8vTHMlyhINzR+58B4k=
github.com/kobergj/go-micro/v4 v4.0.0-20250610135441-d0b187215699/go.mod h1:eE/tD53n3KbVrzrWxKLxdkGw45Fg1qaNLWjpJMvIUF4=
github.com/kobergj/gowebdav v0.0.0-20251030165916-532350997dde h1:HYcp4J4xYe2m9KSUVbTccJb14TpSs+ldCfDFgqsXedI=
@@ -652,8 +655,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs=
github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v1.14.33 h1:A5blZ5ulQo2AtayQ9/limgHEkFreKj1Dv226a1K73s0=
github.com/mattn/go-sqlite3 v1.14.33/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/maxymania/go-system v0.0.0-20170110133659-647cc364bf0b h1:Q53idHrTuQDDHyXaxZ6pUl0I9uyD6Z6uKFK3ocX6LzI=
github.com/maxymania/go-system v0.0.0-20170110133659-647cc364bf0b/go.mod h1:KirJrATYGbTyUwVR26xIkaipRqRcMRXBf8N5dacvGus=
@@ -665,14 +668,14 @@ github.com/miekg/dns v1.1.67 h1:kg0EHj0G4bfT5/oOys6HhZw4vmMlnoZ+gDu8tJ/AlI0=
github.com/miekg/dns v1.1.67/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps=
github.com/mileusna/useragent v1.3.5 h1:SJM5NzBmh/hO+4LGeATKpaEX9+b4vcGg2qXGLiNGDws=
github.com/mileusna/useragent v1.3.5/go.mod h1:3d8TOmwL/5I8pJjyVDteHtgDGcefrFUX4ccGOMKNYYc=
github.com/minio/crc64nvme v1.0.2 h1:6uO1UxGAD+kwqWWp7mBFsi5gAse66C4NXO8cmcVculg=
github.com/minio/crc64nvme v1.0.2/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q=
github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ=
github.com/minio/crc64nvme v1.1.0 h1:e/tAguZ+4cw32D+IO/8GSf5UVr9y+3eJcxZI2WOO/7Q=
github.com/minio/crc64nvme v1.1.0/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
github.com/minio/highwayhash v1.0.4-0.20251030100505-070ab1a87a76 h1:KGuD/pM2JpL9FAYvBrnBBeENKZNh6eNtjqytV6TYjnk=
github.com/minio/highwayhash v1.0.4-0.20251030100505-070ab1a87a76/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
github.com/minio/minio-go/v7 v7.0.95 h1:ywOUPg+PebTMTzn9VDsoFJy32ZuARN9zhB+K3IYEvYU=
github.com/minio/minio-go/v7 v7.0.95/go.mod h1:wOOX3uxS334vImCNRVyIDdXX9OsXDm89ToynKgqUKlo=
github.com/minio/minio-go/v7 v7.0.97 h1:lqhREPyfgHTB/ciX8k2r8k0D93WaFqxbJX36UZq5occ=
github.com/minio/minio-go/v7 v7.0.97/go.mod h1:re5VXuo0pwEtoNLsNuSr0RrLfT/MBtohwdaSmPPSRSk=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
@@ -698,20 +701,20 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nats-io/jwt/v2 v2.8.0 h1:K7uzyz50+yGZDO5o772eRE7atlcSEENpL7P+b74JV1g=
github.com/nats-io/jwt/v2 v2.8.0/go.mod h1:me11pOkwObtcBNR8AiMrUbtVOUGkqYjMQZ6jnSdVUIA=
github.com/nats-io/nats-server/v2 v2.12.1 h1:0tRrc9bzyXEdBLcHr2XEjDzVpUxWx64aZBm7Rl1QDrA=
github.com/nats-io/nats-server/v2 v2.12.1/go.mod h1:OEaOLmu/2e6J9LzUt2OuGjgNem4EpYApO5Rpf26HDs8=
github.com/nats-io/nats.go v1.46.1 h1:bqQ2ZcxVd2lpYI97xYASeRTY3I5boe/IVmuUDPitHfo=
github.com/nats-io/nats.go v1.46.1/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
github.com/nats-io/nkeys v0.4.11 h1:q44qGV008kYd9W1b1nEBkNzvnWxtRSQ7A8BoqRrcfa0=
github.com/nats-io/nkeys v0.4.11/go.mod h1:szDimtgmfOi9n25JpfIdGw12tZFYXqhGxjhVxsatHVE=
github.com/nats-io/nats-server/v2 v2.12.3 h1:KRv+1n7lddMVgkJPQer+pt36TcO0ENxjilBmeWdjcHs=
github.com/nats-io/nats-server/v2 v2.12.3/go.mod h1:MQXjG9WjyXKz9koWzUc3jYUMKD8x3CLmTNy91IQQz3Y=
github.com/nats-io/nats.go v1.48.0 h1:pSFyXApG+yWU/TgbKCjmm5K4wrHu86231/w84qRVR+U=
github.com/nats-io/nats.go v1.48.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
github.com/nats-io/nkeys v0.4.12 h1:nssm7JKOG9/x4J8II47VWCL1Ds29avyiQDRn0ckMvDc=
github.com/nats-io/nkeys v0.4.12/go.mod h1:MT59A1HYcjIcyQDJStTfaOY6vhy9XTUjOFo+SVsvpBg=
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
github.com/oklog/run v1.2.0 h1:O8x3yXwah4A73hJdlrwo/2X6J62gE5qTMusH0dvz60E=
github.com/oklog/run v1.2.0/go.mod h1:mgDbKRSwPhJfesJ4PntqFUbKQRZ50NgmZTSPlFA0YFk=
github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM=
github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
github.com/olekukonko/ll v0.0.9 h1:Y+1YqDfVkqMWuEQMclsF9HUR5+a82+dxJuL1HHSRpxI=
@@ -722,12 +725,12 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns=
github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo=
github.com/onsi/ginkgo/v2 v2.27.4 h1:fcEcQW/A++6aZAZQNUmNjvA9PSOzefMJBerHJ4t8v8Y=
github.com/onsi/ginkgo/v2 v2.27.4/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A=
github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k=
github.com/onsi/gomega v1.38.3 h1:eTX+W6dobAYfFeGC2PV6RwXRu/MyT+cQguijutvkpSM=
github.com/onsi/gomega v1.38.3/go.mod h1:ZCU1pkQcXDO5Sl9/VVEGlDyp+zm0m1cmeG5TOzLgdh4=
github.com/open-policy-agent/opa v1.10.1 h1:haIvxZSPky8HLjRrvQwWAjCPLg8JDFSZMbbG4yyUHgY=
github.com/open-policy-agent/opa v1.10.1/go.mod h1:7uPI3iRpOalJ0BhK6s1JALWPU9HvaV1XeBSSMZnr/PM=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
@@ -737,8 +740,8 @@ github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HD
github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI=
github.com/owncloud/libre-graph-api-go v1.0.5-0.20251107084958-31937a4ea3f1 h1:uW3BUPdaAhti2aP8x3Vb79vzmqAgDaWZ0yrW+4ujjU8=
github.com/owncloud/libre-graph-api-go v1.0.5-0.20251107084958-31937a4ea3f1/go.mod h1:z61VMGAJRtR1nbgXWiNoCkxUXP1B3Je9rMuJbnGd+Og=
github.com/owncloud/reva/v2 v2.0.0-20251107154850-a122a9538794 h1:j5IbfxSCcnMaIi8yei//NsXyHTEsJRz5tJSPnNcCERA=
github.com/owncloud/reva/v2 v2.0.0-20251107154850-a122a9538794/go.mod h1:3PTzYZopTiFJ6DWdZ19HP3iHh2I3Gav0cDNFQ1gMT6g=
github.com/owncloud/reva/v2 v2.0.0-20260116122933-81e6e21256eb h1:G4YhVvtdJDEE0fhV0sWMUDpcpV5WqQNdfLw9xNMfkFI=
github.com/owncloud/reva/v2 v2.0.0-20260116122933-81e6e21256eb/go.mod h1:nDBXQivlwd4Vf5UD/oDT0RakSXy0GJPI1LsyHMm5IpI=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
github.com/pablodz/inotifywaitgo v0.0.9 h1:njquRbBU7fuwIe5rEvtaniVBjwWzcpdUVptSgzFqZsw=
@@ -766,8 +769,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pquerna/cachecontrol v0.2.0 h1:vBXSNuE5MYP9IJ5kjsdo8uq+w41jSPgvba2DEnkRx9k=
github.com/pquerna/cachecontrol v0.2.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI=
github.com/prometheus/alertmanager v0.28.1 h1:BK5pCoAtaKg01BYRUJhEDV1tqJMEtYBGzPw8QdvnnvA=
github.com/prometheus/alertmanager v0.28.1/go.mod h1:0StpPUDDHi1VXeM7p2yYfeZgLVi/PPlt39vo9LQUHxM=
github.com/prometheus/alertmanager v0.30.0 h1:E4dnxSFXK8V2Bb8iqudlisTmaIrF3hRJSWnliG08tBM=
github.com/prometheus/alertmanager v0.30.0/go.mod h1:93PBumcTLr/gNtNtM0m7BcCffbvYP5bKuLBWiOnISaA=
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
@@ -792,8 +795,8 @@ github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/common v0.35.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
github.com/prometheus/common v0.67.4 h1:yR3NqWO1/UyO1w2PhUvXlGQs/PtFmoveVO0KZ4+Lvsc=
github.com/prometheus/common v0.67.4/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI=
github.com/prometheus/procfs v0.0.0-20170703101242-e645f4e5aaa8/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
@@ -840,8 +843,8 @@ github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/sethvargo/go-password v0.3.1 h1:WqrLTjo7X6AcVYfC6R7GtSyuUQR9hGyAj/f1PYQZCJU=
github.com/sethvargo/go-password v0.3.1/go.mod h1:rXofC1zT54N7R8K/h1WDUdkf9BOx5OptoxrMBcrXzvs=
github.com/shamaton/msgpack/v2 v2.3.1 h1:R3QNLIGA/tbdczNMZ5PCRxrXvy+fnzsIaHG4kKMgWYo=
github.com/shamaton/msgpack/v2 v2.3.1/go.mod h1:6khjYnkx73f7VQU7wjcFS9DFjs+59naVWJv1TB7qdOI=
github.com/shamaton/msgpack/v2 v2.4.0 h1:O5Z08MRmbo0lA9o2xnQ4TXx6teJbPqEurqcCOQ8Oi/4=
github.com/shamaton/msgpack/v2 v2.4.0/go.mod h1:6khjYnkx73f7VQU7wjcFS9DFjs+59naVWJv1TB7qdOI=
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c h1:aqg5Vm5dwtvL+YgDpBcK1ITf3o96N/K7/wsRXQnUTEs=
@@ -948,12 +951,12 @@ github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.etcd.io/bbolt v1.4.2 h1:IrUHp260R8c+zYx/Tm8QZr04CX+qWS5PGfPdevhdm1I=
go.etcd.io/bbolt v1.4.2/go.mod h1:Is8rSHO/b4f3XigBC0lL0+4FwAQv3HXEEIgFMuKHceM=
go.etcd.io/etcd/api/v3 v3.6.5 h1:pMMc42276sgR1j1raO/Qv3QI9Af/AuyQUW6CBAWuntA=
go.etcd.io/etcd/api/v3 v3.6.5/go.mod h1:ob0/oWA/UQQlT1BmaEkWQzI0sJ1M0Et0mMpaABxguOQ=
go.etcd.io/etcd/client/pkg/v3 v3.6.5 h1:Duz9fAzIZFhYWgRjp/FgNq2gO1jId9Yae/rLn3RrBP8=
go.etcd.io/etcd/client/pkg/v3 v3.6.5/go.mod h1:8Wx3eGRPiy0qOFMZT/hfvdos+DjEaPxdIDiCDUv/FQk=
go.etcd.io/etcd/client/v3 v3.6.5 h1:yRwZNFBx/35VKHTcLDeO7XVLbCBFbPi+XV4OC3QJf2U=
go.etcd.io/etcd/client/v3 v3.6.5/go.mod h1:ZqwG/7TAFZ0BJ0jXRPoJjKQJtbFo/9NIY8uoFFKcCyo=
go.etcd.io/etcd/api/v3 v3.6.7 h1:7BNJ2gQmc3DNM+9cRkv7KkGQDayElg8x3X+tFDYS+E0=
go.etcd.io/etcd/api/v3 v3.6.7/go.mod h1:xJ81TLj9hxrYYEDmXTeKURMeY3qEDN24hqe+q7KhbnI=
go.etcd.io/etcd/client/pkg/v3 v3.6.7 h1:vvzgyozz46q+TyeGBuFzVuI53/yd133CHceNb/AhBVs=
go.etcd.io/etcd/client/pkg/v3 v3.6.7/go.mod h1:2IVulJ3FZ/czIGl9T4lMF1uxzrhRahLqe+hSgy+Kh7Q=
go.etcd.io/etcd/client/v3 v3.6.7 h1:9WqA5RpIBtdMxAy1ukXLAdtg2pAxNqW5NUoO2wQrE6U=
go.etcd.io/etcd/client/v3 v3.6.7/go.mod h1:2XfROY56AXnUqGsvl+6k29wrwsSbEh1lAouQB1vHpeE=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@@ -962,16 +965,16 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ=
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/google.golang.org/grpc/otelgrpc v0.64.0 h1:RN3ifU8y4prNWeEnQp2kRRHz8UwonAEYZl8tUzHEXAk=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0/go.mod h1:habDz3tEWiFANTo6oUE99EmaFUrCNYAAg3wiVmusm70=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg=
go.opentelemetry.io/contrib/zpages v0.63.0 h1:TppOKuZGbqXMgsfjqq3i09N5Vbo1JLtLImUqiTPGnX4=
go.opentelemetry.io/contrib/zpages v0.63.0/go.mod h1:5F8uugz75ay/MMhRRhxAXY33FuaI8dl7jTxefrIy5qk=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4=
go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
@@ -980,14 +983,14 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4D
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F04bJHUlztTsNGJ2l+6he8c+y/b//eR0jjjemT4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0/go.mod h1:kldtb7jDTeol0l3ewcmd8SDvx3EmIE7lyvqbasU3QC4=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
@@ -1000,8 +1003,8 @@ go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN8
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -1017,8 +1020,8 @@ golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -1060,8 +1063,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1135,8 +1138,8 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1229,8 +1232,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -1285,8 +1288,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
golang.org/x/tools/godoc v0.1.0-deprecated h1:o+aZ1BOj6Hsx/GBdJO/s815sqftjSnrZZwyYTHODvtk=
golang.org/x/tools/godoc v0.1.0-deprecated/go.mod h1:qM63CriJ961IHWmnWa9CjZnBndniPt4a3CK0PVB9bIg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1349,10 +1352,10 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE=
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE=
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY=
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc=
google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 h1:mepRgnBZa07I4TRuomDE4sTIYieg/osKmzIf4USdWS4=
google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -1366,8 +1369,8 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A=
google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c=
google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
google.golang.org/grpc/examples v0.0.0-20211102180624-670c133e568e h1:m7aQHHqd0q89mRwhwS9Bx2rjyl/hsFAeta+uGrHsQaU=
google.golang.org/grpc/examples v0.0.0-20211102180624-670c133e568e/go.mod h1:gID3PKrg7pWKntu9Ss6zTLJ0ttC0X9IHgREOCZwbCVU=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
@@ -1384,8 +1387,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/cenkalti/backoff.v1 v1.1.0 h1:Arh75ttbsvlpVA7WtVpH4u9h6Zl46xuptxqLxPiSo4Y=
gopkg.in/cenkalti/backoff.v1 v1.1.0/go.mod h1:J6Vskwqd+OMVJl8C33mmtxTBs2gyzfv7UDAkHu8BrjI=

View File

@@ -1,7 +1,7 @@
TOML stands for Tom's Obvious, Minimal Language. This Go package provides a
reflection interface similar to Go's standard library `json` and `xml` packages.
Compatible with TOML version [v1.0.0](https://toml.io/en/v1.0.0).
Compatible with TOML version [v1.1.0](https://toml.io/en/v1.1.0).
Documentation: https://pkg.go.dev/github.com/BurntSushi/toml

View File

@@ -206,6 +206,13 @@ func markDecodedRecursive(md *MetaData, tmap map[string]any) {
markDecodedRecursive(md, tmap)
md.context = md.context[0 : len(md.context)-1]
}
if tarr, ok := tmap[key].([]map[string]any); ok {
for _, elm := range tarr {
md.context = append(md.context, key)
markDecodedRecursive(md, elm)
md.context = md.context[0 : len(md.context)-1]
}
}
}
}
@@ -423,7 +430,7 @@ func (md *MetaData) unifyString(data any, rv reflect.Value) error {
if i, ok := data.(int64); ok {
rv.SetString(strconv.FormatInt(i, 10))
} else if f, ok := data.(float64); ok {
rv.SetString(strconv.FormatFloat(f, 'f', -1, 64))
rv.SetString(strconv.FormatFloat(f, 'g', -1, 64))
} else {
return md.badtype("string", data)
}

View File

@@ -228,9 +228,9 @@ func (enc *Encoder) eElement(rv reflect.Value) {
}
switch v.Location() {
default:
enc.wf(v.Format(format))
enc.write(v.Format(format))
case internal.LocalDatetime, internal.LocalDate, internal.LocalTime:
enc.wf(v.In(time.UTC).Format(format))
enc.write(v.In(time.UTC).Format(format))
}
return
case Marshaler:
@@ -279,40 +279,40 @@ func (enc *Encoder) eElement(rv reflect.Value) {
case reflect.String:
enc.writeQuoted(rv.String())
case reflect.Bool:
enc.wf(strconv.FormatBool(rv.Bool()))
enc.write(strconv.FormatBool(rv.Bool()))
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
enc.wf(strconv.FormatInt(rv.Int(), 10))
enc.write(strconv.FormatInt(rv.Int(), 10))
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
enc.wf(strconv.FormatUint(rv.Uint(), 10))
enc.write(strconv.FormatUint(rv.Uint(), 10))
case reflect.Float32:
f := rv.Float()
if math.IsNaN(f) {
if math.Signbit(f) {
enc.wf("-")
enc.write("-")
}
enc.wf("nan")
enc.write("nan")
} else if math.IsInf(f, 0) {
if math.Signbit(f) {
enc.wf("-")
enc.write("-")
}
enc.wf("inf")
enc.write("inf")
} else {
enc.wf(floatAddDecimal(strconv.FormatFloat(f, 'f', -1, 32)))
enc.write(floatAddDecimal(strconv.FormatFloat(f, 'g', -1, 32)))
}
case reflect.Float64:
f := rv.Float()
if math.IsNaN(f) {
if math.Signbit(f) {
enc.wf("-")
enc.write("-")
}
enc.wf("nan")
enc.write("nan")
} else if math.IsInf(f, 0) {
if math.Signbit(f) {
enc.wf("-")
enc.write("-")
}
enc.wf("inf")
enc.write("inf")
} else {
enc.wf(floatAddDecimal(strconv.FormatFloat(f, 'f', -1, 64)))
enc.write(floatAddDecimal(strconv.FormatFloat(f, 'g', -1, 64)))
}
case reflect.Array, reflect.Slice:
enc.eArrayOrSliceElement(rv)
@@ -330,27 +330,32 @@ func (enc *Encoder) eElement(rv reflect.Value) {
// By the TOML spec, all floats must have a decimal with at least one number on
// either side.
func floatAddDecimal(fstr string) string {
if !strings.Contains(fstr, ".") {
return fstr + ".0"
for _, c := range fstr {
if c == 'e' { // Exponent syntax
return fstr
}
if c == '.' {
return fstr
}
}
return fstr
return fstr + ".0"
}
func (enc *Encoder) writeQuoted(s string) {
enc.wf("\"%s\"", dblQuotedReplacer.Replace(s))
enc.write(`"` + dblQuotedReplacer.Replace(s) + `"`)
}
func (enc *Encoder) eArrayOrSliceElement(rv reflect.Value) {
length := rv.Len()
enc.wf("[")
enc.write("[")
for i := 0; i < length; i++ {
elem := eindirect(rv.Index(i))
enc.eElement(elem)
if i != length-1 {
enc.wf(", ")
enc.write(", ")
}
}
enc.wf("]")
enc.write("]")
}
func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) {
@@ -363,7 +368,7 @@ func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) {
continue
}
enc.newline()
enc.wf("%s[[%s]]", enc.indentStr(key), key)
enc.writef("%s[[%s]]", enc.indentStr(key), key)
enc.newline()
enc.eMapOrStruct(key, trv, false)
}
@@ -376,7 +381,7 @@ func (enc *Encoder) eTable(key Key, rv reflect.Value) {
enc.newline()
}
if len(key) > 0 {
enc.wf("%s[%s]", enc.indentStr(key), key)
enc.writef("%s[%s]", enc.indentStr(key), key)
enc.newline()
}
enc.eMapOrStruct(key, rv, false)
@@ -422,7 +427,7 @@ func (enc *Encoder) eMap(key Key, rv reflect.Value, inline bool) {
if inline {
enc.writeKeyValue(Key{mapKey.String()}, val, true)
if trailC || i != len(mapKeys)-1 {
enc.wf(", ")
enc.write(", ")
}
} else {
enc.encode(key.add(mapKey.String()), val)
@@ -431,12 +436,12 @@ func (enc *Encoder) eMap(key Key, rv reflect.Value, inline bool) {
}
if inline {
enc.wf("{")
enc.write("{")
}
writeMapKeys(mapKeysDirect, len(mapKeysSub) > 0)
writeMapKeys(mapKeysSub, false)
if inline {
enc.wf("}")
enc.write("}")
}
}
@@ -534,7 +539,7 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) {
if inline {
enc.writeKeyValue(Key{keyName}, fieldVal, true)
if fieldIndex[0] != totalFields-1 {
enc.wf(", ")
enc.write(", ")
}
} else {
enc.encode(key.add(keyName), fieldVal)
@@ -543,14 +548,14 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) {
}
if inline {
enc.wf("{")
enc.write("{")
}
l := len(fieldsDirect) + len(fieldsSub)
writeFields(fieldsDirect, l)
writeFields(fieldsSub, l)
if inline {
enc.wf("}")
enc.write("}")
}
}
@@ -700,7 +705,7 @@ func isEmpty(rv reflect.Value) bool {
func (enc *Encoder) newline() {
if enc.hasWritten {
enc.wf("\n")
enc.write("\n")
}
}
@@ -722,14 +727,22 @@ func (enc *Encoder) writeKeyValue(key Key, val reflect.Value, inline bool) {
enc.eElement(val)
return
}
enc.wf("%s%s = ", enc.indentStr(key), key.maybeQuoted(len(key)-1))
enc.writef("%s%s = ", enc.indentStr(key), key.maybeQuoted(len(key)-1))
enc.eElement(val)
if !inline {
enc.newline()
}
}
func (enc *Encoder) wf(format string, v ...any) {
func (enc *Encoder) write(s string) {
_, err := enc.w.WriteString(s)
if err != nil {
encPanic(err)
}
enc.hasWritten = true
}
func (enc *Encoder) writef(format string, v ...any) {
_, err := fmt.Fprintf(enc.w, format, v...)
if err != nil {
encPanic(err)

View File

@@ -13,7 +13,6 @@ type itemType int
const (
itemError itemType = iota
itemNIL // used in the parser to indicate no type
itemEOF
itemText
itemString
@@ -47,14 +46,13 @@ func (p Position) String() string {
}
type lexer struct {
input string
start int
pos int
line int
state stateFn
items chan item
tomlNext bool
esc bool
input string
start int
pos int
line int
state stateFn
items chan item
esc bool
// Allow for backing up up to 4 runes. This is necessary because TOML
// contains 3-rune tokens (""" and ''').
@@ -90,14 +88,13 @@ func (lx *lexer) nextItem() item {
}
}
func lex(input string, tomlNext bool) *lexer {
func lex(input string) *lexer {
lx := &lexer{
input: input,
state: lexTop,
items: make(chan item, 10),
stack: make([]stateFn, 0, 10),
line: 1,
tomlNext: tomlNext,
input: input,
state: lexTop,
items: make(chan item, 10),
stack: make([]stateFn, 0, 10),
line: 1,
}
return lx
}
@@ -108,7 +105,7 @@ func (lx *lexer) push(state stateFn) {
func (lx *lexer) pop() stateFn {
if len(lx.stack) == 0 {
return lx.errorf("BUG in lexer: no states to pop")
panic("BUG in lexer: no states to pop")
}
last := lx.stack[len(lx.stack)-1]
lx.stack = lx.stack[0 : len(lx.stack)-1]
@@ -305,6 +302,8 @@ func lexTop(lx *lexer) stateFn {
return lexTableStart
case eof:
if lx.pos > lx.start {
// TODO: never reached? I think this can only occur on a bug in the
// lexer(?)
return lx.errorf("unexpected EOF")
}
lx.emit(itemEOF)
@@ -392,8 +391,6 @@ func lexTableNameStart(lx *lexer) stateFn {
func lexTableNameEnd(lx *lexer) stateFn {
lx.skip(isWhitespace)
switch r := lx.next(); {
case isWhitespace(r):
return lexTableNameEnd
case r == '.':
lx.ignore()
return lexTableNameStart
@@ -412,7 +409,7 @@ func lexTableNameEnd(lx *lexer) stateFn {
// Lexes only one part, e.g. only 'a' inside 'a.b'.
func lexBareName(lx *lexer) stateFn {
r := lx.next()
if isBareKeyChar(r, lx.tomlNext) {
if isBareKeyChar(r) {
return lexBareName
}
lx.backup()
@@ -420,23 +417,23 @@ func lexBareName(lx *lexer) stateFn {
return lx.pop()
}
// lexBareName lexes one part of a key or table.
//
// It assumes that at least one valid character for the table has already been
// read.
// lexQuotedName lexes one part of a quoted key or table name. It assumes that
// it starts lexing at the quote itself (" or ').
//
// Lexes only one part, e.g. only '"a"' inside '"a".b'.
func lexQuotedName(lx *lexer) stateFn {
r := lx.next()
switch {
case isWhitespace(r):
return lexSkip(lx, lexValue)
case r == '"':
lx.ignore() // ignore the '"'
return lexString
case r == '\'':
lx.ignore() // ignore the "'"
return lexRawString
// TODO: I don't think any of the below conditions can ever be reached?
case isWhitespace(r):
return lexSkip(lx, lexValue)
case r == eof:
return lx.errorf("unexpected EOF; expected value")
default:
@@ -464,17 +461,19 @@ func lexKeyStart(lx *lexer) stateFn {
func lexKeyNameStart(lx *lexer) stateFn {
lx.skip(isWhitespace)
switch r := lx.peek(); {
case r == '=' || r == eof:
return lx.errorf("unexpected '='")
case r == '.':
return lx.errorf("unexpected '.'")
default:
lx.push(lexKeyEnd)
return lexBareName
case r == '"' || r == '\'':
lx.ignore()
lx.push(lexKeyEnd)
return lexQuotedName
default:
lx.push(lexKeyEnd)
return lexBareName
// TODO: I think these can never be reached?
case r == '=' || r == eof:
return lx.errorf("unexpected '='")
case r == '.':
return lx.errorf("unexpected '.'")
}
}
@@ -485,7 +484,7 @@ func lexKeyEnd(lx *lexer) stateFn {
switch r := lx.next(); {
case isWhitespace(r):
return lexSkip(lx, lexKeyEnd)
case r == eof:
case r == eof: // TODO: never reached
return lx.errorf("unexpected EOF; expected key separator '='")
case r == '.':
lx.ignore()
@@ -628,10 +627,7 @@ func lexInlineTableValue(lx *lexer) stateFn {
case isWhitespace(r):
return lexSkip(lx, lexInlineTableValue)
case isNL(r):
if lx.tomlNext {
return lexSkip(lx, lexInlineTableValue)
}
return lx.errorPrevLine(errLexInlineTableNL{})
return lexSkip(lx, lexInlineTableValue)
case r == '#':
lx.push(lexInlineTableValue)
return lexCommentStart
@@ -653,10 +649,7 @@ func lexInlineTableValueEnd(lx *lexer) stateFn {
case isWhitespace(r):
return lexSkip(lx, lexInlineTableValueEnd)
case isNL(r):
if lx.tomlNext {
return lexSkip(lx, lexInlineTableValueEnd)
}
return lx.errorPrevLine(errLexInlineTableNL{})
return lexSkip(lx, lexInlineTableValueEnd)
case r == '#':
lx.push(lexInlineTableValueEnd)
return lexCommentStart
@@ -664,10 +657,7 @@ func lexInlineTableValueEnd(lx *lexer) stateFn {
lx.ignore()
lx.skip(isWhitespace)
if lx.peek() == '}' {
if lx.tomlNext {
return lexInlineTableValueEnd
}
return lx.errorf("trailing comma not allowed in inline tables")
return lexInlineTableValueEnd
}
return lexInlineTableValue
case r == '}':
@@ -855,9 +845,6 @@ func lexStringEscape(lx *lexer) stateFn {
r := lx.next()
switch r {
case 'e':
if !lx.tomlNext {
return lx.error(errLexEscape{r})
}
fallthrough
case 'b':
fallthrough
@@ -878,9 +865,6 @@ func lexStringEscape(lx *lexer) stateFn {
case '\\':
return lx.pop()
case 'x':
if !lx.tomlNext {
return lx.error(errLexEscape{r})
}
return lexHexEscape
case 'u':
return lexShortUnicodeEscape
@@ -928,19 +912,9 @@ func lexLongUnicodeEscape(lx *lexer) stateFn {
// lexBaseNumberOrDate can differentiate base prefixed integers from other
// types.
func lexNumberOrDateStart(lx *lexer) stateFn {
r := lx.next()
switch r {
case '0':
if lx.next() == '0' {
return lexBaseNumberOrDate
}
if !isDigit(r) {
// The only way to reach this state is if the value starts
// with a digit, so specifically treat anything else as an
// error.
return lx.errorf("expected a digit but got %q", r)
}
return lexNumberOrDate
}
@@ -1196,13 +1170,13 @@ func lexSkip(lx *lexer, nextState stateFn) stateFn {
}
func (s stateFn) String() string {
if s == nil {
return "<nil>"
}
name := runtime.FuncForPC(reflect.ValueOf(s).Pointer()).Name()
if i := strings.LastIndexByte(name, '.'); i > -1 {
name = name[i+1:]
}
if s == nil {
name = "<nil>"
}
return name + "()"
}
@@ -1210,8 +1184,6 @@ func (itype itemType) String() string {
switch itype {
case itemError:
return "Error"
case itemNIL:
return "NIL"
case itemEOF:
return "EOF"
case itemText:
@@ -1226,18 +1198,22 @@ func (itype itemType) String() string {
return "Float"
case itemDatetime:
return "DateTime"
case itemTableStart:
return "TableStart"
case itemTableEnd:
return "TableEnd"
case itemKeyStart:
return "KeyStart"
case itemKeyEnd:
return "KeyEnd"
case itemArray:
return "Array"
case itemArrayEnd:
return "ArrayEnd"
case itemTableStart:
return "TableStart"
case itemTableEnd:
return "TableEnd"
case itemArrayTableStart:
return "ArrayTableStart"
case itemArrayTableEnd:
return "ArrayTableEnd"
case itemKeyStart:
return "KeyStart"
case itemKeyEnd:
return "KeyEnd"
case itemCommentStart:
return "CommentStart"
case itemInlineTableStart:
@@ -1266,7 +1242,7 @@ func isDigit(r rune) bool { return r >= '0' && r <= '9' }
func isBinary(r rune) bool { return r == '0' || r == '1' }
func isOctal(r rune) bool { return r >= '0' && r <= '7' }
func isHex(r rune) bool { return (r >= '0' && r <= '9') || (r|0x20 >= 'a' && r|0x20 <= 'f') }
func isBareKeyChar(r rune, tomlNext bool) bool {
func isBareKeyChar(r rune) bool {
return (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') ||
(r >= '0' && r <= '9') || r == '_' || r == '-'
}

View File

@@ -3,7 +3,6 @@ package toml
import (
"fmt"
"math"
"os"
"strconv"
"strings"
"time"
@@ -17,7 +16,6 @@ type parser struct {
context Key // Full key for the current hash in scope.
currentKey string // Base key name for everything except hashes.
pos Position // Current position in the TOML file.
tomlNext bool
ordered []Key // List of keys in the order that they appear in the TOML data.
@@ -32,8 +30,6 @@ type keyInfo struct {
}
func parse(data string) (p *parser, err error) {
_, tomlNext := os.LookupEnv("BURNTSUSHI_TOML_110")
defer func() {
if r := recover(); r != nil {
if pErr, ok := r.(ParseError); ok {
@@ -73,10 +69,9 @@ func parse(data string) (p *parser, err error) {
p = &parser{
keyInfo: make(map[string]keyInfo),
mapping: make(map[string]any),
lx: lex(data, tomlNext),
lx: lex(data),
ordered: make([]Key, 0),
implicits: make(map[string]struct{}),
tomlNext: tomlNext,
}
for {
item := p.next()
@@ -350,17 +345,14 @@ func (p *parser) valueFloat(it item) (any, tomlType) {
var dtTypes = []struct {
fmt string
zone *time.Location
next bool
}{
{time.RFC3339Nano, time.Local, false},
{"2006-01-02T15:04:05.999999999", internal.LocalDatetime, false},
{"2006-01-02", internal.LocalDate, false},
{"15:04:05.999999999", internal.LocalTime, false},
// tomlNext
{"2006-01-02T15:04Z07:00", time.Local, true},
{"2006-01-02T15:04", internal.LocalDatetime, true},
{"15:04", internal.LocalTime, true},
{time.RFC3339Nano, time.Local},
{"2006-01-02T15:04:05.999999999", internal.LocalDatetime},
{"2006-01-02", internal.LocalDate},
{"15:04:05.999999999", internal.LocalTime},
{"2006-01-02T15:04Z07:00", time.Local},
{"2006-01-02T15:04", internal.LocalDatetime},
{"15:04", internal.LocalTime},
}
func (p *parser) valueDatetime(it item) (any, tomlType) {
@@ -371,9 +363,6 @@ func (p *parser) valueDatetime(it item) (any, tomlType) {
err error
)
for _, dt := range dtTypes {
if dt.next && !p.tomlNext {
continue
}
t, err = time.ParseInLocation(dt.fmt, it.val, dt.zone)
if err == nil {
if missingLeadingZero(it.val, dt.fmt) {
@@ -644,6 +633,11 @@ func (p *parser) setValue(key string, value any) {
// Note that since it has already been defined (as a hash), we don't
// want to overwrite it. So our business is done.
if p.isArray(keyContext) {
if !p.isImplicit(keyContext) {
if _, ok := hash[key]; ok {
p.panicf("Key '%s' has already been defined.", keyContext)
}
}
p.removeImplicit(keyContext)
hash[key] = value
return
@@ -802,10 +796,8 @@ func (p *parser) replaceEscapes(it item, str string) string {
b.WriteByte(0x0d)
skip = 1
case 'e':
if p.tomlNext {
b.WriteByte(0x1b)
skip = 1
}
b.WriteByte(0x1b)
skip = 1
case '"':
b.WriteByte(0x22)
skip = 1
@@ -815,11 +807,9 @@ func (p *parser) replaceEscapes(it item, str string) string {
// The lexer guarantees the correct number of characters are present;
// don't need to check here.
case 'x':
if p.tomlNext {
escaped := p.asciiEscapeToUnicode(it, str[i+2:i+4])
b.WriteRune(escaped)
skip = 3
}
escaped := p.asciiEscapeToUnicode(it, str[i+2:i+4])
b.WriteRune(escaped)
skip = 3
case 'u':
escaped := p.asciiEscapeToUnicode(it, str[i+2:i+6])
b.WriteRune(escaped)

View File

@@ -103,8 +103,8 @@ func getMemoryLimitV2(chs []cgroupHierarchy, mis []mountInfo) (uint64, error) {
return 0, err
}
// retrieve the memory limit from the memory.max file
return readMemoryLimitV2FromPath(filepath.Join(cgroupPath, "memory.max"))
// retrieve the memory limit from the memory.max recursively.
return walkCgroupV2Hierarchy(cgroupPath, mountPoint)
}
// readMemoryLimitV2FromPath reads the memory limit for cgroup v2 from the given path.
@@ -131,6 +131,39 @@ func readMemoryLimitV2FromPath(path string) (uint64, error) {
return limit, nil
}
// walkCgroupV2Hierarchy walks up the cgroup v2 hierarchy to find the most restrictive memory limit.
func walkCgroupV2Hierarchy(cgroupPath, mountPoint string) (uint64, error) {
var (
found = false
minLimit uint64 = math.MaxUint64
currentPath = cgroupPath
)
for {
limit, err := readMemoryLimitV2FromPath(filepath.Join(currentPath, "memory.max"))
if err != nil && !errors.Is(err, ErrNoLimit) {
return 0, err
} else if err == nil {
found = true
minLimit = min(minLimit, limit)
}
if currentPath == mountPoint {
break
}
parent := filepath.Dir(currentPath)
if parent == currentPath {
break
}
currentPath = parent
}
if !found {
return 0, ErrNoLimit
}
return minLimit, nil
}
// getMemoryLimitV1 retrieves the memory limit from the cgroup v1 controller.
func getMemoryLimitV1(chs []cgroupHierarchy, mis []mountInfo) (uint64, error) {
// find the cgroup v1 path for the memory controller.

View File

@@ -17,13 +17,18 @@
// [test properties]: https://antithesis.com/docs/using_antithesis/properties/
// [workload]: https://antithesis.com/docs/getting_started/first_test/
// [antithesis-go-generator]: https://antithesis.com/docs/using_antithesis/sdk/go/instrumentor/
// [triage report]: https://antithesis.com/docs/reports/triage/
// [triage report]: https://antithesis.com/docs/reports/
// [here]: https://antithesis.com/docs/using_antithesis/sdk/fallback/
// [Sometimes assertions]: https://antithesis.com/docs/best_practices/sometimes_assertions/
//
// [details]: https://antithesis.com/docs/reports/triage/#details
// [details]: https://antithesis.com/docs/reports/
package assert
import (
"encoding/json"
"fmt"
)
type assertInfo struct {
Location *locationInfo `json:"location"`
Details map[string]any `json:"details"`
@@ -36,6 +41,64 @@ type assertInfo struct {
Condition bool `json:"condition"`
}
// Create a custom json marshaler for assertInfo so that we can force Errors to be marshaled with their error details.
// Without this, custom errors are marshaled as an empty object because the default json marshaler doesn't include the error
// (because it's a method - not an exported struct field).
func (f assertInfo) MarshalJSON() ([]byte, error) {
type alias assertInfo // prevent infinite recursion
a := alias(f)
if a.Details != nil {
a.Details = normalizeMap(a.Details)
}
return json.Marshal(a)
}
type jsonError struct {
innerError error
}
func (e jsonError) MarshalJSON() ([]byte, error) {
// Marshal this as the debug output string instead of e.Error(). These should be equivalent, but Sprintf correctly
// handles nil values for us (which otherwise are annoying to defend against due to this - https://go.dev/doc/faq#nil_error)
return json.Marshal(fmt.Sprintf("%+v", e.innerError))
}
// Recursively replace any `error` with jsonError while doing a deep copy.
// Most of the logic is in the normalize method below. This method exists to localize the type assertions
// and provide a function that takes in/out a map instead of any.
func normalizeMap(v map[string]any) map[string]any {
return normalize(v).(map[string]any)
}
func normalize(input any) any {
// This switch will miss some cases (pointers, structs, non-any types), but should catch a very large proportion of real error interfaces
// in real details objects. We can augment this if we find other cases common enough to support.
switch inputTyped := input.(type) {
case error:
// Check if the underlying error implements json.Marshaler, so that if the error
// already knows who to marshal itself, we don't override that.
if _, ok := inputTyped.(json.Marshaler); ok {
return inputTyped
} else {
return jsonError{inputTyped}
}
case map[string]any:
out := make(map[string]any, len(inputTyped))
for k, v := range inputTyped {
out[k] = normalize(v)
}
return out
case []any:
out := make([]any, len(inputTyped))
for i := range inputTyped {
out[i] = normalize(inputTyped[i])
}
return out
default:
return input
}
}
type wrappedAssertInfo struct {
A *assertInfo `json:"antithesis_assert"`
}

View File

@@ -4,67 +4,11 @@ package internal
import (
"encoding/json"
"fmt"
"log"
"math/rand"
"os"
"unsafe"
)
// --------------------------------------------------------------------------------
// To build and run an executable with this package
//
// CC=clang CGO_ENABLED=1 go run ./main.go
// --------------------------------------------------------------------------------
// \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
//
// The commented lines below, and the `import "C"` line which must directly follow
// the commented lines are used by CGO. They are load-bearing, and should not be
// changed without first understanding how CGO uses them.
//
// \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
// #cgo LDFLAGS: -ldl
//
// #include <dlfcn.h>
// #include <stdbool.h>
// #include <stdint.h>
// #include <stdlib.h>
//
// typedef void (*go_fuzz_json_data_fn)(const char *data, size_t size);
// void
// go_fuzz_json_data(void *f, const char *data, size_t size) {
// ((go_fuzz_json_data_fn)f)(data, size);
// }
//
// typedef void (*go_fuzz_flush_fn)(void);
// void
// go_fuzz_flush(void *f) {
// ((go_fuzz_flush_fn)f)();
// }
//
// typedef uint64_t (*go_fuzz_get_random_fn)(void);
// uint64_t
// go_fuzz_get_random(void *f) {
// return ((go_fuzz_get_random_fn)f)();
// }
//
// typedef bool (*go_notify_coverage_fn)(size_t);
// int
// go_notify_coverage(void *f, size_t edges) {
// bool b = ((go_notify_coverage_fn)f)(edges);
// return b ? 1 : 0;
// }
//
// typedef uint64_t (*go_init_coverage_fn)(size_t num_edges, const char *symbols);
// uint64_t
// go_init_coverage(void *f, size_t num_edges, const char *symbols) {
// return ((go_init_coverage_fn)f)(num_edges, symbols);
// }
//
import "C"
func Json_data(v any) error {
if data, err := json.Marshal(v); err != nil {
return err
@@ -95,45 +39,10 @@ type libHandler interface {
const (
errorLogLinePrefix = "[* antithesis-sdk-go *]"
defaultNativeLibraryPath = "/usr/lib/libvoidstar.so"
)
var handler libHandler
type voidstarHandler struct {
fuzzJsonData unsafe.Pointer
fuzzFlush unsafe.Pointer
fuzzGetRandom unsafe.Pointer
initCoverage unsafe.Pointer
notifyCoverage unsafe.Pointer
}
func (h *voidstarHandler) output(message string) {
msg_len := len(message)
if msg_len == 0 {
return
}
cstrMessage := C.CString(message)
defer C.free(unsafe.Pointer(cstrMessage))
C.go_fuzz_json_data(h.fuzzJsonData, cstrMessage, C.ulong(msg_len))
C.go_fuzz_flush(h.fuzzFlush)
}
func (h *voidstarHandler) random() uint64 {
return uint64(C.go_fuzz_get_random(h.fuzzGetRandom))
}
func (h *voidstarHandler) init_coverage(num_edge uint64, symbols string) uint64 {
cstrSymbols := C.CString(symbols)
defer C.free(unsafe.Pointer(cstrSymbols))
return uint64(C.go_init_coverage(h.initCoverage, C.ulong(num_edge), cstrSymbols))
}
func (h *voidstarHandler) notify(edge uint64) bool {
ival := int(C.go_notify_coverage(h.notifyCoverage, C.ulong(edge)))
return ival == 1
}
type localHandler struct {
outputFile *os.File // can be nil
}
@@ -160,63 +69,12 @@ func (h *localHandler) init_coverage(num_edges uint64, symbols string) uint64 {
return 0
}
// If we have a file at `defaultNativeLibraryPath`, we load the shared library
// (and panic on any error encountered during load).
// Otherwise fallback to the local handler.
func init() {
if _, err := os.Stat(defaultNativeLibraryPath); err == nil {
if handler, err = openSharedLib(defaultNativeLibraryPath); err != nil {
panic(err)
}
return
handler = init_in_antithesis()
if handler == nil {
// Otherwise fallback to the local handler.
handler = openLocalHandler()
}
handler = openLocalHandler()
}
// Attempt to load libvoidstar and some symbols from `path`
func openSharedLib(path string) (*voidstarHandler, error) {
cstrPath := C.CString(path)
defer C.free(unsafe.Pointer(cstrPath))
dlError := func(message string) error {
return fmt.Errorf("%s: (%s)", message, C.GoString(C.dlerror()))
}
sharedLib := C.dlopen(cstrPath, C.int(C.RTLD_NOW))
if sharedLib == nil {
return nil, dlError("Can not load the Antithesis native library")
}
loadFunc := func(name string) (symbol unsafe.Pointer, err error) {
cstrName := C.CString(name)
defer C.free(unsafe.Pointer(cstrName))
if symbol = C.dlsym(sharedLib, cstrName); symbol == nil {
err = dlError(fmt.Sprintf("Can not access symbol %s", name))
}
return
}
fuzzJsonData, err := loadFunc("fuzz_json_data")
if err != nil {
return nil, err
}
fuzzFlush, err := loadFunc("fuzz_flush")
if err != nil {
return nil, err
}
fuzzGetRandom, err := loadFunc("fuzz_get_random")
if err != nil {
return nil, err
}
notifyCoverage, err := loadFunc("notify_coverage")
if err != nil {
return nil, err
}
initCoverage, err := loadFunc("init_coverage_module")
if err != nil {
return nil, err
}
return &voidstarHandler{fuzzJsonData, fuzzFlush, fuzzGetRandom, initCoverage, notifyCoverage}, nil
}
// If `localOutputEnvVar` is set to a non-empty path, attempt to open that path and truncate the file

View File

@@ -3,7 +3,7 @@ package internal
// --------------------------------------------------------------------------------
// Versions
// --------------------------------------------------------------------------------
const SDK_Version = "0.4.3"
const SDK_Version = "0.5.0"
const Protocol_Version = "1.1.0"
// --------------------------------------------------------------------------------

View File

@@ -0,0 +1,160 @@
//go:build enable_antithesis_sdk && linux && amd64 && cgo
package internal
import (
"fmt"
"unsafe"
"os"
)
// --------------------------------------------------------------------------------
// To build and run an executable with this package
//
// CC=clang CGO_ENABLED=1 go run ./main.go
// --------------------------------------------------------------------------------
// \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
//
// The commented lines below, and the `import "C"` line which must directly follow
// the commented lines are used by CGO. They are load-bearing, and should not be
// changed without first understanding how CGO uses them.
//
// \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
// #cgo LDFLAGS: -ldl
//
// #include <dlfcn.h>
// #include <stdbool.h>
// #include <stdint.h>
// #include <stdlib.h>
//
// typedef void (*go_fuzz_json_data_fn)(const char *data, size_t size);
// void
// go_fuzz_json_data(void *f, const char *data, size_t size) {
// ((go_fuzz_json_data_fn)f)(data, size);
// }
//
// typedef void (*go_fuzz_flush_fn)(void);
// void
// go_fuzz_flush(void *f) {
// ((go_fuzz_flush_fn)f)();
// }
//
// typedef uint64_t (*go_fuzz_get_random_fn)(void);
// uint64_t
// go_fuzz_get_random(void *f) {
// return ((go_fuzz_get_random_fn)f)();
// }
//
// typedef bool (*go_notify_coverage_fn)(size_t);
// int
// go_notify_coverage(void *f, size_t edges) {
// bool b = ((go_notify_coverage_fn)f)(edges);
// return b ? 1 : 0;
// }
//
// typedef uint64_t (*go_init_coverage_fn)(size_t num_edges, const char *symbols);
// uint64_t
// go_init_coverage(void *f, size_t num_edges, const char *symbols) {
// return ((go_init_coverage_fn)f)(num_edges, symbols);
// }
//
import "C"
const (
defaultNativeLibraryPath = "/usr/lib/libvoidstar.so"
)
type voidstarHandler struct {
fuzzJsonData unsafe.Pointer
fuzzFlush unsafe.Pointer
fuzzGetRandom unsafe.Pointer
initCoverage unsafe.Pointer
notifyCoverage unsafe.Pointer
}
func (h *voidstarHandler) output(message string) {
msg_len := len(message)
if msg_len == 0 {
return
}
cstrMessage := C.CString(message)
defer C.free(unsafe.Pointer(cstrMessage))
C.go_fuzz_json_data(h.fuzzJsonData, cstrMessage, C.ulong(msg_len))
C.go_fuzz_flush(h.fuzzFlush)
}
func (h *voidstarHandler) random() uint64 {
return uint64(C.go_fuzz_get_random(h.fuzzGetRandom))
}
func (h *voidstarHandler) init_coverage(num_edge uint64, symbols string) uint64 {
cstrSymbols := C.CString(symbols)
defer C.free(unsafe.Pointer(cstrSymbols))
return uint64(C.go_init_coverage(h.initCoverage, C.ulong(num_edge), cstrSymbols))
}
func (h *voidstarHandler) notify(edge uint64) bool {
ival := int(C.go_notify_coverage(h.notifyCoverage, C.ulong(edge)))
return ival == 1
}
// Attempt to load libvoidstar and some symbols from `path`
func openSharedLib(path string) (*voidstarHandler, error) {
cstrPath := C.CString(path)
defer C.free(unsafe.Pointer(cstrPath))
dlError := func(message string) error {
return fmt.Errorf("%s: (%s)", message, C.GoString(C.dlerror()))
}
sharedLib := C.dlopen(cstrPath, C.int(C.RTLD_NOW))
if sharedLib == nil {
return nil, dlError("Can not load the Antithesis native library")
}
loadFunc := func(name string) (symbol unsafe.Pointer, err error) {
cstrName := C.CString(name)
defer C.free(unsafe.Pointer(cstrName))
if symbol = C.dlsym(sharedLib, cstrName); symbol == nil {
err = dlError(fmt.Sprintf("Can not access symbol %s", name))
}
return
}
fuzzJsonData, err := loadFunc("fuzz_json_data")
if err != nil {
return nil, err
}
fuzzFlush, err := loadFunc("fuzz_flush")
if err != nil {
return nil, err
}
fuzzGetRandom, err := loadFunc("fuzz_get_random")
if err != nil {
return nil, err
}
notifyCoverage, err := loadFunc("notify_coverage")
if err != nil {
return nil, err
}
initCoverage, err := loadFunc("init_coverage_module")
if err != nil {
return nil, err
}
return &voidstarHandler{fuzzJsonData, fuzzFlush, fuzzGetRandom, initCoverage, notifyCoverage}, nil
}
// If we have a file at `defaultNativeLibraryPath`, we load the shared library
// (and panic on any error encountered during load).
func init_in_antithesis() libHandler {
if _, err := os.Stat(defaultNativeLibraryPath); err == nil {
handler, err := openSharedLib(defaultNativeLibraryPath)
if err != nil {
panic(err)
}
return handler
}
return nil
}

View File

@@ -0,0 +1,7 @@
//go:build enable_antithesis_sdk && (!linux || !amd64 || !cgo)
package internal
func init_in_antithesis() libHandler {
return nil
}

View File

@@ -40,6 +40,8 @@ var (
// ErrOpNotSupported is returned in general for operations that are not
// supported.
ErrOpNotSupported = getError(-C.EOPNOTSUPP)
// ErrNotImplemented indicates a function is not implemented in by libcephfs.
ErrNotImplemented = getError(-C.ENOSYS)
// Private errors:

View File

@@ -1,5 +1,3 @@
//go:build ceph_preview
package cephfs
// Fd returns the integer open file descriptor in cephfs.

View File

@@ -1,5 +1,3 @@
//go:build ceph_preview
package cephfs
// Futime changes file/directory last access and modification times.

268
vendor/github.com/ceph/go-ceph/cephfs/snap_diff.go generated vendored Normal file
View File

@@ -0,0 +1,268 @@
//go:build ceph_preview
package cephfs
/*
#cgo LDFLAGS: -lcephfs
#cgo CPPFLAGS: -D_FILE_OFFSET_BITS=64
#include <stdlib.h>
#include <dirent.h>
#include <cephfs/libcephfs.h>
// Types and constants are copied from libcephfs.h with added "_" as prefix. This
// prevents redefinition of the types on libcephfs versions that have them
// already.
typedef struct {
struct dirent dir_entry;
uint64_t snapid;
} _ceph_snapdiff_entry_t;
typedef struct {
struct ceph_mount_info* cmount;
struct ceph_dir_result* dir1;
struct ceph_dir_result* dir_aux;
} _ceph_snapdiff_info;
// open_snapdiff_fn matches the open_snapdiff function signature.
typedef int(*open_snapdiff_fn)(struct ceph_mount_info* cmount,
const char* root_path,
const char* rel_path,
const char* snap1,
const char* snap2,
_ceph_snapdiff_info* out);
// open_snapdiff_dlsym take *fn as open_snapdiff_fn and calls the dynamically loaded
// open_snapdiff function passed as 1st argument.
static inline int open_snapdiff_dlsym(void *fn,
struct ceph_mount_info* cmount,
const char* root_path,
const char* rel_path,
const char* snap1,
const char* snap2,
_ceph_snapdiff_info* out) {
// cast function pointer fn to open_snapdiff and call the function
return ((open_snapdiff_fn) fn)(cmount, root_path, rel_path, snap1, snap2, out);
}
// readdir_snapdiff_fn matches the readdir_snapdiff function signature.
typedef int(*readdir_snapdiff_fn)(_ceph_snapdiff_info* snapdiff,
_ceph_snapdiff_entry_t* out);
// readdir_snapdiff_dlsym take *fn as readdir_snapdiff_fn and calls the dynamically loaded
// readdir_snapdiff function passed as 1st argument.
static inline int readdir_snapdiff_dlsym(void *fn,
_ceph_snapdiff_info* snapdiff,
_ceph_snapdiff_entry_t* out) {
// cast function pointer fn to readdir_snapdiff and call the function
return ((readdir_snapdiff_fn) fn)(snapdiff, out);
}
// close_snapdiff_fn matches the close_snapdiff function signature.
typedef int(*close_snapdiff_fn)(_ceph_snapdiff_info* snapdiff);
// close_snapdiff_dlsym take *fn as close_snapdiff_fn and calls the dynamically loaded
// close_snapdiff function passed as 1st argument.
static inline int close_snapdiff_dlsym(void *fn,
_ceph_snapdiff_info* snapdiff) {
// cast function pointer fn to close_snapdiff and call the function
return ((close_snapdiff_fn) fn)(snapdiff);
}
*/
import "C"
import (
"fmt"
"sync"
"unsafe"
"github.com/ceph/go-ceph/internal/dlsym"
)
var (
cephOpenSnapDiffOnce sync.Once
cephReaddirSnapDiffOnce sync.Once
cephCloseSnapDiffOnce sync.Once
cephOpenSnapDiff unsafe.Pointer
cephReaddirSnapDiff unsafe.Pointer
cephCloseSnapDiff unsafe.Pointer
cephOpenSnapDiffErr error
cephReaddirSnapDiffErr error
cephCloseSnapDiffErr error
)
// SnapDiffInfo is a handle to a snapshot diff API.
type SnapDiffInfo struct {
cMount *MountInfo
dir1 *Directory
dirAux *Directory
}
// SnapDiffEntry is a single entry in the snapshot diff.
// It contains a DirEntry and the ID of the snapshot to
// which the directory belongs.
type SnapDiffEntry struct {
DirEntry *DirEntry
SnapID uint64
}
// SnapDiffConfig is used to define the parameters of a open_snapdiff call.
// Snapshot Delta is generated between the passed snapshots snap1 and snap2.
// All fields must be specified before passing to OpenSnapDiff().
type SnapDiffConfig struct {
// CMount is the ceph mount handle that will be used for snap.diff retrieval.
CMount *MountInfo
// RootPath represents the root path for snapshots-in-question.
RootPath string
// RelPath is the subpath under the root to build delta for.
RelPath string
// Snap1 is the first snapshot name.
Snap1 string
// Snap2 is the second snapshot name.
Snap2 string
}
// OpenSnapDiff opens a snapshot diff stream to get snapshots delta
// and returns a SnapDiffInfo struct containing the diff information.
//
// Implements:
//
// int ceph_open_snapdiff(struct ceph_mount_info* cmount,
// const char* root_path,
// const char* rel_path,
// const char* snap1,
// const char* snap2,
// struct ceph_snapdiff_info* out);
func OpenSnapDiff(config SnapDiffConfig) (*SnapDiffInfo, error) {
if config.CMount == nil || config.RootPath == "" || config.RelPath == "" ||
config.Snap1 == "" || config.Snap2 == "" {
return nil, errInvalid
}
cephOpenSnapDiffOnce.Do(func() {
cephOpenSnapDiff, cephOpenSnapDiffErr = dlsym.LookupSymbol("ceph_open_snapdiff")
})
if cephOpenSnapDiffErr != nil {
return nil, fmt.Errorf("%w: %w", ErrNotImplemented, cephOpenSnapDiffErr)
}
rawCephSnapDiffInfo := &C._ceph_snapdiff_info{}
ret := C.open_snapdiff_dlsym(
cephOpenSnapDiff,
config.CMount.mount,
C.CString(config.RootPath),
C.CString(config.RelPath),
C.CString(config.Snap1),
C.CString(config.Snap2),
rawCephSnapDiffInfo)
if ret != 0 {
return nil, getError(ret)
}
mountInfo := &MountInfo{
mount: rawCephSnapDiffInfo.cmount,
}
cephSnapDiffInfo := &SnapDiffInfo{
cMount: mountInfo,
dir1: &Directory{
mount: mountInfo,
dir: rawCephSnapDiffInfo.dir1,
},
dirAux: &Directory{
mount: mountInfo,
dir: rawCephSnapDiffInfo.dir_aux,
},
}
return cephSnapDiffInfo, nil
}
// validate checks that the SnapDiffInfo struct is valid.
func (info *SnapDiffInfo) validate() error {
if info.cMount == nil || info.dir1 == nil || info.dirAux == nil {
return errInvalid
}
return nil
}
// Readdir returns the next entry in the snapshot diff stream.
// If there are no more entries, it returns (nil, nil).
//
// Implements:
//
// int ceph_readdir_snapdiff(struct ceph_snapdiff_info* snapdiff,
// struct ceph_snapdiff_entry_t* out);
func (info *SnapDiffInfo) Readdir() (*SnapDiffEntry, error) {
if err := info.validate(); err != nil {
return nil, err
}
cephReaddirSnapDiffOnce.Do(func() {
cephReaddirSnapDiff, cephReaddirSnapDiffErr = dlsym.LookupSymbol("ceph_readdir_snapdiff")
})
if cephReaddirSnapDiffErr != nil {
return nil, fmt.Errorf("%w: %w", ErrNotImplemented, cephReaddirSnapDiffErr)
}
rawSnapDiffEntry := &C._ceph_snapdiff_entry_t{}
rawSnapDiffInfo := &C._ceph_snapdiff_info{
cmount: info.cMount.mount,
dir1: info.dir1.dir,
dir_aux: info.dirAux.dir,
}
ret := C.readdir_snapdiff_dlsym(
cephReaddirSnapDiff,
rawSnapDiffInfo,
rawSnapDiffEntry)
if ret < 0 {
return nil, getError(ret)
}
if ret == 0 {
// return 0 indicates there is not more entries to return.
return nil, nil
}
snapDiffEntry := &SnapDiffEntry{
DirEntry: toDirEntry(&rawSnapDiffEntry.dir_entry),
SnapID: uint64(rawSnapDiffEntry.snapid),
}
return snapDiffEntry, nil
}
// Close closes the snapshot diff handle.
//
// Implements:
//
// int ceph_close_snapdiff(struct ceph_snapdiff_info* snapdiff);
func (info *SnapDiffInfo) Close() error {
if err := info.validate(); err != nil {
return err
}
cephCloseSnapDiffOnce.Do(func() {
cephCloseSnapDiff, cephCloseSnapDiffErr = dlsym.LookupSymbol("ceph_close_snapdiff")
})
if cephCloseSnapDiffErr != nil {
return fmt.Errorf("%w: %w", ErrNotImplemented, cephCloseSnapDiffErr)
}
rawCephSnapDiffInfo := &C._ceph_snapdiff_info{
cmount: info.cMount.mount,
dir1: info.dir1.dir,
dir_aux: info.dirAux.dir,
}
ret := C.close_snapdiff_dlsym(
cephCloseSnapDiff,
rawCephSnapDiffInfo)
if ret != 0 {
return getError(ret)
}
return nil
}

39
vendor/github.com/ceph/go-ceph/internal/dlsym/dlsym.go generated vendored Normal file
View File

@@ -0,0 +1,39 @@
package dlsym
// #cgo LDFLAGS: -ldl
//
// #define _GNU_SOURCE
//
// #include <stdlib.h>
// #include <dlfcn.h>
import "C"
import (
"errors"
"fmt"
"unsafe"
)
// ErrUndefinedSymbol is returned by LookupSymbol when the requested symbol
// could not be found.
var ErrUndefinedSymbol = errors.New("symbol not found")
// LookupSymbol resolves the named symbol from the already dynamically loaded
// libraries. If the symbol is found, a pointer to it is returned, in case of a
// failure, the message provided by dlerror() is included in the error message.
func LookupSymbol(symbol string) (unsafe.Pointer, error) {
cSymName := C.CString(symbol)
defer C.free(unsafe.Pointer(cSymName))
// clear dlerror before looking up the symbol
C.dlerror()
// resolve the address of the symbol
sym := C.dlsym(C.RTLD_DEFAULT, cSymName)
e := C.dlerror()
dlerr := C.GoString(e)
if dlerr != "" {
return nil, fmt.Errorf("%w: %s", ErrUndefinedSymbol, dlerr)
}
return sym, nil
}

View File

@@ -162,7 +162,7 @@ var supportedAlgorithms = map[string]bool{
// parsing.
//
// // Directly fetch the metadata document.
// resp, err := http.Get("https://login.example.com/custom-metadata-path")
// resp, err := http.Get("https://login.example.com/custom-metadata-path")
// if err != nil {
// // ...
// }
@@ -267,7 +267,7 @@ func NewProvider(ctx context.Context, issuer string) (*Provider, error) {
issuerURL = issuer
}
if p.Issuer != issuerURL && !skipIssuerValidation {
return nil, fmt.Errorf("oidc: issuer did not match the issuer returned by provider, expected %q got %q", issuer, p.Issuer)
return nil, fmt.Errorf("oidc: issuer URL provided to client (%q) did not match the issuer URL returned by provider (%q)", issuer, p.Issuer)
}
var algs []string
for _, a := range p.Algorithms {

View File

@@ -41,6 +41,6 @@ const (
)
// Print prints a message to the local systemd journal using Send().
func Print(priority Priority, format string, a ...interface{}) error {
func Print(priority Priority, format string, a ...any) error {
return Send(fmt.Sprintf(format, a...), priority, nil)
}

View File

@@ -13,7 +13,6 @@
// limitations under the License.
//go:build !windows
// +build !windows
// Package journal provides write bindings to the local systemd journal.
// It is implemented in pure Go and connects to the journal directly over its
@@ -31,7 +30,6 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"net"
"os"
"strconv"
@@ -194,7 +192,7 @@ func appendVariable(w io.Writer, name, value string) {
* - the data, followed by a newline
*/
fmt.Fprintln(w, name)
binary.Write(w, binary.LittleEndian, uint64(len(value)))
_ = binary.Write(w, binary.LittleEndian, uint64(len(value)))
fmt.Fprintln(w, value)
} else {
/* just write the variable and value all on one line */
@@ -214,7 +212,7 @@ func validVarName(name string) error {
}
for _, c := range name {
if !(('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_') {
if ('A' > c || c > 'Z') && ('0' > c || c > '9') && c != '_' {
return errors.New("Variable name contains invalid characters")
}
}
@@ -239,7 +237,7 @@ func isSocketSpaceError(err error) bool {
// tempFd creates a temporary, unlinked file under `/dev/shm`.
func tempFd() (*os.File, error) {
file, err := ioutil.TempFile("/dev/shm/", "journal.XXXXX")
file, err := os.CreateTemp("/dev/shm/", "journal.XXXXX")
if err != nil {
return nil, err
}

View File

@@ -0,0 +1,9 @@
# Github is obeying this ignore file by default.
# Run this command on local to ignore formatting commits in `git blame`
# git config blame.ignoreRevsFile .git-blame-ignore-revs
# Added a new column to supported_mimes.md
# The supported_mimes.md file was a nice way to find when a file format was
# introduced. However, when I changed to add a new column in the table, the
# whole git blame got poisoned for the file.
eb497f9bc5d31c6eab2929a112051218670137ba

View File

@@ -1,5 +1,41 @@
version: "2"
run:
timeout: 5m
linters:
exclusions:
presets:
- std-error-handling
enable:
- gosec # Detects security problems.
# Keep all extras disabled for now to focus on the integer overflow problem.
# TODO: enable these and other good linters
- dogsled # Detects assignments with too many blank identifiers.
- errcheck
- errchkjson # Detects unsupported types passed to json encoding functions and reports if checks for the returned error can be omitted.
- exhaustive # Detects missing options in enum switch statements.
- gocyclo
- govet
- ineffassign
- makezero # Finds slice declarations with non-zero initial length.
- misspell # Detects commonly misspelled English words in comments.
- nakedret # Detects uses of naked returns.
- prealloc # Detects slice declarations that could potentially be pre-allocated.
- predeclared # Detects code that shadows one of Go's predeclared identifiers.
- reassign # Detects reassigning a top-level variable in another package.
- staticcheck
- thelper # Detects test helpers without t.Helper().
- tparallel # Detects inappropriate usage of t.Parallel().
- unconvert # Detects unnecessary type conversions.
- unused
- usestdlibvars # Detects the possibility to use variables/constants from the Go standard library.
- usetesting # Reports uses of functions with replacement inside the testing package.
settings:
govet:
disable:
- stdversion
gosec:
excludes:
- G404 # Weak random number generator used in tests.
- G304 # File inclusion

View File

@@ -70,13 +70,13 @@ If increasing the limit does not help, please
## Tests
In addition to unit tests,
[mimetype_tests](https://github.com/gabriel-vasile/mimetype_tests) compares the
library with the [Unix file utility](https://en.wikipedia.org/wiki/File_(command))
library with [libmagic](https://en.wikipedia.org/wiki/File_(command))
for around 50 000 sample files. Check the latest comparison results
[here](https://github.com/gabriel-vasile/mimetype_tests/actions).
## Benchmarks
Benchmarks for each file format are performed when a PR is open. The results can
be seen on the [workflows page](https://github.com/gabriel-vasile/mimetype/actions/workflows/benchmark.yml).
Benchmarks are performed when a PR is open. The results can be seen on the
[workflows page](https://github.com/gabriel-vasile/mimetype/actions/workflows/benchmark.yml).
Performance improvements are welcome but correctness is prioritized.
## Structure
@@ -97,7 +97,9 @@ or from a [file](https://pkg.go.dev/github.com/gabriel-vasile/mimetype#DetectFil
</div>
## Contributing
Contributions are unexpected but welcome. When submitting a PR for detection of
a new file format, please make sure to add a record to the list of testcases
from [mimetype_test.go](mimetype_test.go). For complex files a record can be added
in the [testdata](testdata) directory.
Contributions are never expected but very much welcome.
[mimetype_tests](https://github.com/gabriel-vasile/mimetype_tests/actions/workflows/test.yml)
shows which file formats are most often misidentified and can help prioritise.
When submitting a PR for detection of a new file format, please make sure to
add a record to the list of testcases in [mimetype_test.go](mimetype_test.go).
For complex files a record can be added in the [testdata](testdata) directory.

View File

@@ -2,6 +2,7 @@ package charset
import (
"bytes"
"strings"
"unicode/utf8"
"github.com/gabriel-vasile/mimetype/internal/markup"
@@ -141,27 +142,25 @@ func FromXML(content []byte) string {
return FromPlain(content)
}
func fromXML(s scan.Bytes) string {
xml := []byte("<?XML")
xml := []byte("<?xml")
lxml := len(xml)
for {
if len(s) == 0 {
return ""
}
for scan.ByteIsWS(s.Peek()) {
s.Advance(1)
}
s.TrimLWS()
if len(s) <= lxml {
return ""
}
if !s.Match(xml, scan.IgnoreCase) {
s = s[1:] // safe to slice instead of s.Advance(1) because bounds are checked
continue
i, k := s.Search(xml, 0)
if i == -1 {
return ""
}
aName, aVal, hasMore := "", "", true
s.Advance(i + k)
var aName, aVal []byte
hasMore := true
for hasMore {
aName, aVal, hasMore = markup.GetAnAttribute(&s)
if aName == "encoding" && aVal != "" {
return aVal
if scan.Bytes(aName).Match([]byte("encoding"), 0) != -1 && len(aVal) != 0 {
return string(aVal)
}
}
}
@@ -198,10 +197,10 @@ func fromHTML(s scan.Bytes) string {
return ""
}
// Abort when <body is reached.
if s.Match(body, scan.IgnoreCase) {
if s.Match(body, scan.IgnoreCase) != -1 {
return ""
}
if !s.Match(meta, scan.IgnoreCase) {
if s.Match(meta, scan.IgnoreCase) == -1 {
s = s[1:] // safe to slice instead of s.Advance(1) because bounds are checked
continue
}
@@ -215,14 +214,16 @@ func fromHTML(s scan.Bytes) string {
needPragma := dontKnow
charset := ""
aName, aVal, hasMore := "", "", true
var aNameB, aValB []byte
hasMore := true
for hasMore {
aName, aVal, hasMore = markup.GetAnAttribute(&s)
aNameB, aValB, hasMore = markup.GetAnAttribute(&s)
aName := strings.ToLower(string(aNameB))
if attrList[aName] {
continue
}
// processing step
if len(aName) == 0 && len(aVal) == 0 {
if len(aName) == 0 && len(aValB) == 0 {
if needPragma == dontKnow {
continue
}
@@ -231,15 +232,18 @@ func fromHTML(s scan.Bytes) string {
}
}
attrList[aName] = true
if aName == "http-equiv" && scan.Bytes(aVal).Match([]byte("CONTENT-TYPE"), scan.IgnoreCase) {
gotPragma = true
} else if aName == "content" {
charset = string(extractCharsetFromMeta(scan.Bytes(aVal)))
switch aName {
case "http-equiv":
if scan.Bytes(aValB).Match([]byte("CONTENT-TYPE"), scan.IgnoreCase) != -1 {
gotPragma = true
}
case "content":
charset = string(extractCharsetFromMeta(scan.Bytes(aValB)))
if len(charset) != 0 {
needPragma = doNeedPragma
}
} else if aName == "charset" {
charset = aVal
case "charset":
charset = string(aValB)
needPragma = doNotNeedPragma
}
}

View File

@@ -94,20 +94,6 @@ func eq(path1, path2 [][]byte) bool {
return true
}
// LooksLikeObjectOrArray reports if first non white space character from raw
// is either { or [. Parsing raw as JSON is a heavy operation. When receiving some
// text input we can skip parsing if the input does not even look like JSON.
func LooksLikeObjectOrArray(raw []byte) bool {
for i := range raw {
if isSpace(raw[i]) {
continue
}
return raw[i] == '{' || raw[i] == '['
}
return false
}
// Parse will take out a parser from the pool depending on queryType and tries
// to parse raw bytes as JSON.
func Parse(queryType string, raw []byte) (parsed, inspected, firstToken int, querySatisfied bool) {
@@ -257,8 +243,11 @@ out:
return 0
}
// openArray is used instead of an inline []byte{'['} to avoid mem alllocs.
var openArray = []byte{'['}
func (p *parserState) consumeArray(b []byte, qs []query, lvl int) (n int) {
p.appendPath([]byte{'['}, qs)
p.appendPath(openArray, qs)
if len(b) == 0 {
return 0
}

View File

@@ -5,43 +5,80 @@ import (
"encoding/binary"
)
var (
// SevenZ matches a 7z archive.
SevenZ = prefix([]byte{0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C})
// Gzip matches gzip files based on http://www.zlib.org/rfc-gzip.html#header-trailer.
Gzip = prefix([]byte{0x1f, 0x8b})
// Fits matches an Flexible Image Transport System file.
Fits = prefix([]byte{
// SevenZ matches a 7z archive.
func SevenZ(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte{0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C})
}
// Gzip matches gzip files based on http://www.zlib.org/rfc-gzip.html#header-trailer.
func Gzip(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte{0x1f, 0x8b})
}
// Fits matches an Flexible Image Transport System file.
func Fits(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte{
0x53, 0x49, 0x4D, 0x50, 0x4C, 0x45, 0x20, 0x20, 0x3D, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54,
})
// Xar matches an eXtensible ARchive format file.
Xar = prefix([]byte{0x78, 0x61, 0x72, 0x21})
// Bz2 matches a bzip2 file.
Bz2 = prefix([]byte{0x42, 0x5A, 0x68})
// Ar matches an ar (Unix) archive file.
Ar = prefix([]byte{0x21, 0x3C, 0x61, 0x72, 0x63, 0x68, 0x3E})
// Deb matches a Debian package file.
Deb = offset([]byte{
}
// Xar matches an eXtensible ARchive format file.
func Xar(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte{0x78, 0x61, 0x72, 0x21})
}
// Bz2 matches a bzip2 file.
func Bz2(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte{0x42, 0x5A, 0x68})
}
// Ar matches an ar (Unix) archive file.
func Ar(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte{0x21, 0x3C, 0x61, 0x72, 0x63, 0x68, 0x3E})
}
// Deb matches a Debian package file.
func Deb(raw []byte, _ uint32) bool {
return offset(raw, []byte{
0x64, 0x65, 0x62, 0x69, 0x61, 0x6E, 0x2D,
0x62, 0x69, 0x6E, 0x61, 0x72, 0x79,
}, 8)
// Warc matches a Web ARChive file.
Warc = prefix([]byte("WARC/1.0"), []byte("WARC/1.1"))
// Cab matches a Microsoft Cabinet archive file.
Cab = prefix([]byte("MSCF\x00\x00\x00\x00"))
// Xz matches an xz compressed stream based on https://tukaani.org/xz/xz-file-format.txt.
Xz = prefix([]byte{0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00})
// Lzip matches an Lzip compressed file.
Lzip = prefix([]byte{0x4c, 0x5a, 0x49, 0x50})
// RPM matches an RPM or Delta RPM package file.
RPM = prefix([]byte{0xed, 0xab, 0xee, 0xdb}, []byte("drpm"))
// Cpio matches a cpio archive file.
Cpio = prefix([]byte("070707"), []byte("070701"), []byte("070702"))
// RAR matches a RAR archive file.
RAR = prefix([]byte("Rar!\x1A\x07\x00"), []byte("Rar!\x1A\x07\x01\x00"))
)
}
// Warc matches a Web ARChive file.
func Warc(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte("WARC/1.0")) ||
bytes.HasPrefix(raw, []byte("WARC/1.1"))
}
// Cab matches a Microsoft Cabinet archive file.
func Cab(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte("MSCF\x00\x00\x00\x00"))
}
// Xz matches an xz compressed stream based on https://tukaani.org/xz/xz-file-format.txt.
func Xz(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte{0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00})
}
// Lzip matches an Lzip compressed file.
func Lzip(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte{0x4c, 0x5a, 0x49, 0x50})
}
// RPM matches an RPM or Delta RPM package file.
func RPM(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte{0xed, 0xab, 0xee, 0xdb}) ||
bytes.HasPrefix(raw, []byte("drpm"))
}
// RAR matches a RAR archive file.
func RAR(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte("Rar!\x1A\x07\x00")) ||
bytes.HasPrefix(raw, []byte("Rar!\x1A\x07\x01\x00"))
}
// InstallShieldCab matches an InstallShield Cabinet archive file.
func InstallShieldCab(raw []byte, _ uint32) bool {
@@ -69,15 +106,26 @@ func CRX(raw []byte, limit uint32) bool {
if len(raw) < minHeaderLen || !bytes.HasPrefix(raw, []byte("Cr24")) {
return false
}
pubkeyLen := binary.LittleEndian.Uint32(raw[8:12])
sigLen := binary.LittleEndian.Uint32(raw[12:16])
pubkeyLen := int64(binary.LittleEndian.Uint32(raw[8:12]))
sigLen := int64(binary.LittleEndian.Uint32(raw[12:16]))
zipOffset := minHeaderLen + pubkeyLen + sigLen
if uint32(len(raw)) < zipOffset {
if zipOffset < 0 || int64(len(raw)) < zipOffset {
return false
}
return Zip(raw[zipOffset:], limit)
}
// Cpio matches a cpio archive file.
func Cpio(raw []byte, _ uint32) bool {
if len(raw) < 6 {
return false
}
return binary.LittleEndian.Uint16(raw) == 070707 || // binary cpio
bytes.HasPrefix(raw, []byte("070707")) || // portable ASCII cpios
bytes.HasPrefix(raw, []byte("070701")) ||
bytes.HasPrefix(raw, []byte("070702"))
}
// Tar matches a (t)ape (ar)chive file.
// Tar files are divided into 512 bytes records. First record contains a 257
// bytes header padded with NUL.
@@ -161,3 +209,13 @@ func tarChksum(b []byte) (unsigned, signed int64) {
}
return unsigned, signed
}
// Zlib matches zlib compressed files.
func Zlib(raw []byte, _ uint32) bool {
// https://www.ietf.org/rfc/rfc6713.txt
// This check has one fault: ASCII code can satisfy it; for ex: []byte("x ")
zlib := len(raw) > 1 &&
raw[0] == 'x' && binary.BigEndian.Uint16(raw)%31 == 0
// Check that the file is not a regular text to avoid false positives.
return zlib && !Text(raw, 0)
}

View File

@@ -5,26 +5,50 @@ import (
"encoding/binary"
)
var (
// Flac matches a Free Lossless Audio Codec file.
Flac = prefix([]byte("\x66\x4C\x61\x43\x00\x00\x00\x22"))
// Midi matches a Musical Instrument Digital Interface file.
Midi = prefix([]byte("\x4D\x54\x68\x64"))
// Ape matches a Monkey's Audio file.
Ape = prefix([]byte("\x4D\x41\x43\x20\x96\x0F\x00\x00\x34\x00\x00\x00\x18\x00\x00\x00\x90\xE3"))
// MusePack matches a Musepack file.
MusePack = prefix([]byte("MPCK"))
// Au matches a Sun Microsystems au file.
Au = prefix([]byte("\x2E\x73\x6E\x64"))
// Amr matches an Adaptive Multi-Rate file.
Amr = prefix([]byte("\x23\x21\x41\x4D\x52"))
// Voc matches a Creative Voice file.
Voc = prefix([]byte("Creative Voice File"))
// M3u matches a Playlist file.
M3u = prefix([]byte("#EXTM3U"))
// AAC matches an Advanced Audio Coding file.
AAC = prefix([]byte{0xFF, 0xF1}, []byte{0xFF, 0xF9})
)
// Flac matches a Free Lossless Audio Codec file.
func Flac(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte("\x66\x4C\x61\x43\x00\x00\x00\x22"))
}
// Midi matches a Musical Instrument Digital Interface file.
func Midi(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte("\x4D\x54\x68\x64"))
}
// Ape matches a Monkey's Audio file.
func Ape(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte("\x4D\x41\x43\x20\x96\x0F\x00\x00\x34\x00\x00\x00\x18\x00\x00\x00\x90\xE3"))
}
// MusePack matches a Musepack file.
func MusePack(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte("MPCK"))
}
// Au matches a Sun Microsystems au file.
func Au(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte("\x2E\x73\x6E\x64"))
}
// Amr matches an Adaptive Multi-Rate file.
func Amr(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte("\x23\x21\x41\x4D\x52"))
}
// Voc matches a Creative Voice file.
func Voc(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte("Creative Voice File"))
}
// M3u matches a Playlist file.
func M3u(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte("#EXTM3U"))
}
// AAC matches an Advanced Audio Coding file.
func AAC(raw []byte, _ uint32) bool {
return len(raw) > 1 && ((raw[0] == 0xFF && raw[1] == 0xF1) || (raw[0] == 0xFF && raw[1] == 0xF9))
}
// Mp3 matches an mp3 file.
func Mp3(raw []byte, limit uint32) bool {

View File

@@ -6,26 +6,52 @@ import (
"encoding/binary"
)
var (
// Lnk matches Microsoft lnk binary format.
Lnk = prefix([]byte{0x4C, 0x00, 0x00, 0x00, 0x01, 0x14, 0x02, 0x00})
// Wasm matches a web assembly File Format file.
Wasm = prefix([]byte{0x00, 0x61, 0x73, 0x6D})
// Exe matches a Windows/DOS executable file.
Exe = prefix([]byte{0x4D, 0x5A})
// Elf matches an Executable and Linkable Format file.
Elf = prefix([]byte{0x7F, 0x45, 0x4C, 0x46})
// Nes matches a Nintendo Entertainment system ROM file.
Nes = prefix([]byte{0x4E, 0x45, 0x53, 0x1A})
// SWF matches an Adobe Flash swf file.
SWF = prefix([]byte("CWS"), []byte("FWS"), []byte("ZWS"))
// Torrent has bencoded text in the beginning.
Torrent = prefix([]byte("d8:announce"))
// PAR1 matches a parquet file.
Par1 = prefix([]byte{0x50, 0x41, 0x52, 0x31})
// CBOR matches a Concise Binary Object Representation https://cbor.io/
CBOR = prefix([]byte{0xD9, 0xD9, 0xF7})
)
// Lnk matches Microsoft lnk binary format.
func Lnk(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte{0x4C, 0x00, 0x00, 0x00, 0x01, 0x14, 0x02, 0x00})
}
// Wasm matches a web assembly File Format file.
func Wasm(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte{0x00, 0x61, 0x73, 0x6D})
}
// Exe matches a Windows/DOS executable file.
func Exe(raw []byte, _ uint32) bool {
return len(raw) > 1 && raw[0] == 0x4D && raw[1] == 0x5A
}
// Elf matches an Executable and Linkable Format file.
func Elf(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte{0x7F, 0x45, 0x4C, 0x46})
}
// Nes matches a Nintendo Entertainment system ROM file.
func Nes(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte{0x4E, 0x45, 0x53, 0x1A})
}
// SWF matches an Adobe Flash swf file.
func SWF(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte("CWS")) ||
bytes.HasPrefix(raw, []byte("FWS")) ||
bytes.HasPrefix(raw, []byte("ZWS"))
}
// Torrent has bencoded text in the beginning.
func Torrent(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte("d8:announce"))
}
// PAR1 matches a parquet file.
func Par1(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte{0x50, 0x41, 0x52, 0x31})
}
// CBOR matches a Concise Binary Object Representation https://cbor.io/
func CBOR(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte{0xD9, 0xD9, 0xF7})
}
// Java bytecode and Mach-O binaries share the same magic number.
// More info here https://github.com/threatstack/libmagic/blob/master/magic/Magdir/cafebabe
@@ -168,8 +194,10 @@ func Marc(raw []byte, limit uint32) bool {
//
// [glTF specification]: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html
// [IANA glTF entry]: https://www.iana.org/assignments/media-types/model/gltf-binary
var GLB = prefix([]byte("\x67\x6C\x54\x46\x02\x00\x00\x00"),
[]byte("\x67\x6C\x54\x46\x01\x00\x00\x00"))
func GLB(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte("\x67\x6C\x54\x46\x02\x00\x00\x00")) ||
bytes.HasPrefix(raw, []byte("\x67\x6C\x54\x46\x01\x00\x00\x00"))
}
// TzIf matches a Time Zone Information Format (TZif) file.
// See more: https://tools.ietf.org/id/draft-murchison-tzdist-tzif-00.html#rfc.section.3

View File

@@ -1,13 +1,21 @@
package magic
var (
// Sqlite matches an SQLite database file.
Sqlite = prefix([]byte{
import "bytes"
// Sqlite matches an SQLite database file.
func Sqlite(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte{
0x53, 0x51, 0x4c, 0x69, 0x74, 0x65, 0x20, 0x66,
0x6f, 0x72, 0x6d, 0x61, 0x74, 0x20, 0x33, 0x00,
})
// MsAccessAce matches Microsoft Access dababase file.
MsAccessAce = offset([]byte("Standard ACE DB"), 4)
// MsAccessMdb matches legacy Microsoft Access database file (JET, 2003 and earlier).
MsAccessMdb = offset([]byte("Standard Jet DB"), 4)
)
}
// MsAccessAce matches Microsoft Access dababase file.
func MsAccessAce(raw []byte, _ uint32) bool {
return offset(raw, []byte("Standard ACE DB"), 4)
}
// MsAccessMdb matches legacy Microsoft Access database file (JET, 2003 and earlier).
func MsAccessMdb(raw []byte, _ uint32) bool {
return offset(raw, []byte("Standard Jet DB"), 4)
}

View File

@@ -5,14 +5,31 @@ import (
"encoding/binary"
)
var (
// Fdf matches a Forms Data Format file.
Fdf = prefix([]byte("%FDF"))
// Mobi matches a Mobi file.
Mobi = offset([]byte("BOOKMOBI"), 60)
// Lit matches a Microsoft Lit file.
Lit = prefix([]byte("ITOLITLS"))
)
// Pdf matches a Portable Document Format file.
// https://github.com/file/file/blob/11010cc805546a3e35597e67e1129a481aed40e8/magic/Magdir/pdf
func Pdf(raw []byte, _ uint32) bool {
// usual pdf signature
return bytes.HasPrefix(raw, []byte("%PDF-")) ||
// new-line prefixed signature
bytes.HasPrefix(raw, []byte("\012%PDF-")) ||
// UTF-8 BOM prefixed signature
bytes.HasPrefix(raw, []byte("\xef\xbb\xbf%PDF-"))
}
// Fdf matches a Forms Data Format file.
func Fdf(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte("%FDF"))
}
// Mobi matches a Mobi file.
func Mobi(raw []byte, _ uint32) bool {
return offset(raw, []byte("BOOKMOBI"), 60)
}
// Lit matches a Microsoft Lit file.
func Lit(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte("ITOLITLS"))
}
// PDF matches a Portable Document Format file.
// The %PDF- header should be the first thing inside the file but many

View File

@@ -4,14 +4,20 @@ import (
"bytes"
)
var (
// Woff matches a Web Open Font Format file.
Woff = prefix([]byte("wOFF"))
// Woff2 matches a Web Open Font Format version 2 file.
Woff2 = prefix([]byte("wOF2"))
// Otf matches an OpenType font file.
Otf = prefix([]byte{0x4F, 0x54, 0x54, 0x4F, 0x00})
)
// Woff matches a Web Open Font Format file.
func Woff(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte("wOFF"))
}
// Woff2 matches a Web Open Font Format version 2 file.
func Woff2(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte("wOF2"))
}
// Otf matches an OpenType font file.
func Otf(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte{0x4F, 0x54, 0x54, 0x4F, 0x00})
}
// Ttf matches a TrueType font file.
func Ttf(raw []byte, limit uint32) bool {

View File

@@ -4,24 +4,33 @@ import (
"bytes"
)
var (
// AVIF matches an AV1 Image File Format still or animated.
// Wikipedia page seems outdated listing image/avif-sequence for animations.
// https://github.com/AOMediaCodec/av1-avif/issues/59
AVIF = ftyp([]byte("avif"), []byte("avis"))
// ThreeGP matches a 3GPP file.
ThreeGP = ftyp(
// AVIF matches an AV1 Image File Format still or animated.
// Wikipedia page seems outdated listing image/avif-sequence for animations.
// https://github.com/AOMediaCodec/av1-avif/issues/59
func AVIF(raw []byte, _ uint32) bool {
return ftyp(raw, []byte("avif"), []byte("avis"))
}
// ThreeGP matches a 3GPP file.
func ThreeGP(raw []byte, _ uint32) bool {
return ftyp(raw,
[]byte("3gp1"), []byte("3gp2"), []byte("3gp3"), []byte("3gp4"),
[]byte("3gp5"), []byte("3gp6"), []byte("3gp7"), []byte("3gs7"),
[]byte("3ge6"), []byte("3ge7"), []byte("3gg6"),
)
// ThreeG2 matches a 3GPP2 file.
ThreeG2 = ftyp(
}
// ThreeG2 matches a 3GPP2 file.
func ThreeG2(raw []byte, _ uint32) bool {
return ftyp(raw,
[]byte("3g24"), []byte("3g25"), []byte("3g26"), []byte("3g2a"),
[]byte("3g2b"), []byte("3g2c"), []byte("KDDI"),
)
// AMp4 matches an audio MP4 file.
AMp4 = ftyp(
}
// AMp4 matches an audio MP4 file.
func AMp4(raw []byte, _ uint32) bool {
return ftyp(raw,
// audio for Adobe Flash Player 9+
[]byte("F4A "), []byte("F4B "),
// Apple iTunes AAC-LC (.M4A) Audio
@@ -31,33 +40,61 @@ var (
// Nero Digital AAC Audio
[]byte("NDAS"),
)
// Mqv matches a Sony / Mobile QuickTime file.
Mqv = ftyp([]byte("mqt "))
// M4a matches an audio M4A file.
M4a = ftyp([]byte("M4A "))
// M4v matches an Appl4 M4V video file.
M4v = ftyp([]byte("M4V "), []byte("M4VH"), []byte("M4VP"))
// Heic matches a High Efficiency Image Coding (HEIC) file.
Heic = ftyp([]byte("heic"), []byte("heix"))
// HeicSequence matches a High Efficiency Image Coding (HEIC) file sequence.
HeicSequence = ftyp([]byte("hevc"), []byte("hevx"))
// Heif matches a High Efficiency Image File Format (HEIF) file.
Heif = ftyp([]byte("mif1"), []byte("heim"), []byte("heis"), []byte("avic"))
// HeifSequence matches a High Efficiency Image File Format (HEIF) file sequence.
HeifSequence = ftyp([]byte("msf1"), []byte("hevm"), []byte("hevs"), []byte("avcs"))
// Mj2 matches a Motion JPEG 2000 file: https://en.wikipedia.org/wiki/Motion_JPEG_2000.
Mj2 = ftyp([]byte("mj2s"), []byte("mjp2"), []byte("MFSM"), []byte("MGSV"))
// Dvb matches a Digital Video Broadcasting file: https://dvb.org.
// https://cconcolato.github.io/mp4ra/filetype.html
// https://github.com/file/file/blob/512840337ead1076519332d24fefcaa8fac36e06/magic/Magdir/animation#L135-L154
Dvb = ftyp(
}
// Mqv matches a Sony / Mobile QuickTime file.
func Mqv(raw []byte, _ uint32) bool {
return ftyp(raw, []byte("mqt "))
}
// M4a matches an audio M4A file.
func M4a(raw []byte, _ uint32) bool {
return ftyp(raw, []byte("M4A "))
}
// M4v matches an Appl4 M4V video file.
func M4v(raw []byte, _ uint32) bool {
return ftyp(raw, []byte("M4V "), []byte("M4VH"), []byte("M4VP"))
}
// Heic matches a High Efficiency Image Coding (HEIC) file.
func Heic(raw []byte, _ uint32) bool {
return ftyp(raw, []byte("heic"), []byte("heix"))
}
// HeicSequence matches a High Efficiency Image Coding (HEIC) file sequence.
func HeicSequence(raw []byte, _ uint32) bool {
return ftyp(raw, []byte("hevc"), []byte("hevx"))
}
// Heif matches a High Efficiency Image File Format (HEIF) file.
func Heif(raw []byte, _ uint32) bool {
return ftyp(raw, []byte("mif1"), []byte("heim"), []byte("heis"), []byte("avic"))
}
// HeifSequence matches a High Efficiency Image File Format (HEIF) file sequence.
func HeifSequence(raw []byte, _ uint32) bool {
return ftyp(raw, []byte("msf1"), []byte("hevm"), []byte("hevs"), []byte("avcs"))
}
// Mj2 matches a Motion JPEG 2000 file: https://en.wikipedia.org/wiki/Motion_JPEG_2000.
func Mj2(raw []byte, _ uint32) bool {
return ftyp(raw, []byte("mj2s"), []byte("mjp2"), []byte("MFSM"), []byte("MGSV"))
}
// Dvb matches a Digital Video Broadcasting file: https://dvb.org.
// https://cconcolato.github.io/mp4ra/filetype.html
// https://github.com/file/file/blob/512840337ead1076519332d24fefcaa8fac36e06/magic/Magdir/animation#L135-L154
func Dvb(raw []byte, _ uint32) bool {
return ftyp(raw,
[]byte("dby1"), []byte("dsms"), []byte("dts1"), []byte("dts2"),
[]byte("dts3"), []byte("dxo "), []byte("dmb1"), []byte("dmpf"),
[]byte("drc1"), []byte("dv1a"), []byte("dv1b"), []byte("dv2a"),
[]byte("dv2b"), []byte("dv3a"), []byte("dv3b"), []byte("dvr1"),
[]byte("dvt1"), []byte("emsg"))
// TODO: add support for remaining video formats at ftyps.com.
)
}
// TODO: add support for remaining video formats at ftyps.com.
// QuickTime matches a QuickTime File Format file.
// https://www.loc.gov/preservation/digital/formats/fdd/fdd000052.shtml

View File

@@ -2,66 +2,127 @@ package magic
import "bytes"
var (
// Png matches a Portable Network Graphics file.
// https://www.w3.org/TR/PNG/
Png = prefix([]byte{0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A})
// Apng matches an Animated Portable Network Graphics file.
// https://wiki.mozilla.org/APNG_Specification
Apng = offset([]byte("acTL"), 37)
// Jpg matches a Joint Photographic Experts Group file.
Jpg = prefix([]byte{0xFF, 0xD8, 0xFF})
// Jp2 matches a JPEG 2000 Image file (ISO 15444-1).
Jp2 = jpeg2k([]byte{0x6a, 0x70, 0x32, 0x20})
// Jpx matches a JPEG 2000 Image file (ISO 15444-2).
Jpx = jpeg2k([]byte{0x6a, 0x70, 0x78, 0x20})
// Jpm matches a JPEG 2000 Image file (ISO 15444-6).
Jpm = jpeg2k([]byte{0x6a, 0x70, 0x6D, 0x20})
// Gif matches a Graphics Interchange Format file.
Gif = prefix([]byte("GIF87a"), []byte("GIF89a"))
// Bmp matches a bitmap image file.
Bmp = prefix([]byte{0x42, 0x4D})
// Ps matches a PostScript file.
Ps = prefix([]byte("%!PS-Adobe-"))
// Psd matches a Photoshop Document file.
Psd = prefix([]byte("8BPS"))
// Ico matches an ICO file.
Ico = prefix([]byte{0x00, 0x00, 0x01, 0x00}, []byte{0x00, 0x00, 0x02, 0x00})
// Icns matches an ICNS (Apple Icon Image format) file.
Icns = prefix([]byte("icns"))
// Tiff matches a Tagged Image File Format file.
Tiff = prefix([]byte{0x49, 0x49, 0x2A, 0x00}, []byte{0x4D, 0x4D, 0x00, 0x2A})
// Bpg matches a Better Portable Graphics file.
Bpg = prefix([]byte{0x42, 0x50, 0x47, 0xFB})
// Xcf matches GIMP image data.
Xcf = prefix([]byte("gimp xcf"))
// Pat matches GIMP pattern data.
Pat = offset([]byte("GPAT"), 20)
// Gbr matches GIMP brush data.
Gbr = offset([]byte("GIMP"), 20)
// Hdr matches Radiance HDR image.
// https://web.archive.org/web/20060913152809/http://local.wasp.uwa.edu.au/~pbourke/dataformats/pic/
Hdr = prefix([]byte("#?RADIANCE\n"))
// Xpm matches X PixMap image data.
Xpm = prefix([]byte{0x2F, 0x2A, 0x20, 0x58, 0x50, 0x4D, 0x20, 0x2A, 0x2F})
// Jxs matches a JPEG XS coded image file (ISO/IEC 21122-3).
Jxs = prefix([]byte{0x00, 0x00, 0x00, 0x0C, 0x4A, 0x58, 0x53, 0x20, 0x0D, 0x0A, 0x87, 0x0A})
// Jxr matches Microsoft HD JXR photo file.
Jxr = prefix([]byte{0x49, 0x49, 0xBC, 0x01})
)
// Png matches a Portable Network Graphics file.
// https://www.w3.org/TR/PNG/
func Png(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte{0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A})
}
func jpeg2k(sig []byte) Detector {
return func(raw []byte, _ uint32) bool {
if len(raw) < 24 {
return false
}
// Apng matches an Animated Portable Network Graphics file.
// https://wiki.mozilla.org/APNG_Specification
func Apng(raw []byte, _ uint32) bool {
return offset(raw, []byte("acTL"), 37)
}
if !bytes.Equal(raw[4:8], []byte{0x6A, 0x50, 0x20, 0x20}) &&
!bytes.Equal(raw[4:8], []byte{0x6A, 0x50, 0x32, 0x20}) {
return false
}
return bytes.Equal(raw[20:24], sig)
// Jpg matches a Joint Photographic Experts Group file.
func Jpg(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte{0xFF, 0xD8, 0xFF})
}
// Jp2 matches a JPEG 2000 Image file (ISO 15444-1).
func Jp2(raw []byte, _ uint32) bool {
return jpeg2k(raw, []byte{0x6a, 0x70, 0x32, 0x20})
}
// Jpx matches a JPEG 2000 Image file (ISO 15444-2).
func Jpx(raw []byte, _ uint32) bool {
return jpeg2k(raw, []byte{0x6a, 0x70, 0x78, 0x20})
}
// Jpm matches a JPEG 2000 Image file (ISO 15444-6).
func Jpm(raw []byte, _ uint32) bool {
return jpeg2k(raw, []byte{0x6a, 0x70, 0x6D, 0x20})
}
// Gif matches a Graphics Interchange Format file.
func Gif(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte("GIF87a")) ||
bytes.HasPrefix(raw, []byte("GIF89a"))
}
// Bmp matches a bitmap image file.
func Bmp(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte{0x42, 0x4D})
}
// Ps matches a PostScript file.
func Ps(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte("%!PS-Adobe-"))
}
// Psd matches a Photoshop Document file.
func Psd(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte("8BPS"))
}
// Ico matches an ICO file.
func Ico(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte{0x00, 0x00, 0x01, 0x00}) ||
bytes.HasPrefix(raw, []byte{0x00, 0x00, 0x02, 0x00})
}
// Icns matches an ICNS (Apple Icon Image format) file.
func Icns(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte("icns"))
}
// Tiff matches a Tagged Image File Format file.
func Tiff(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte{0x49, 0x49, 0x2A, 0x00}) ||
bytes.HasPrefix(raw, []byte{0x4D, 0x4D, 0x00, 0x2A})
}
// Bpg matches a Better Portable Graphics file.
func Bpg(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte{0x42, 0x50, 0x47, 0xFB})
}
// Xcf matches GIMP image data.
func Xcf(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte("gimp xcf"))
}
// Pat matches GIMP pattern data.
func Pat(raw []byte, _ uint32) bool {
return offset(raw, []byte("GPAT"), 20)
}
// Gbr matches GIMP brush data.
func Gbr(raw []byte, _ uint32) bool {
return offset(raw, []byte("GIMP"), 20)
}
// Hdr matches Radiance HDR image.
// https://web.archive.org/web/20060913152809/http://local.wasp.uwa.edu.au/~pbourke/dataformats/pic/
func Hdr(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte("#?RADIANCE\n"))
}
// Xpm matches X PixMap image data.
func Xpm(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte{0x2F, 0x2A, 0x20, 0x58, 0x50, 0x4D, 0x20, 0x2A, 0x2F})
}
// Jxs matches a JPEG XS coded image file (ISO/IEC 21122-3).
func Jxs(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte{0x00, 0x00, 0x00, 0x0C, 0x4A, 0x58, 0x53, 0x20, 0x0D, 0x0A, 0x87, 0x0A})
}
// Jxr matches Microsoft HD JXR photo file.
func Jxr(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte{0x49, 0x49, 0xBC, 0x01})
}
func jpeg2k(raw []byte, sig []byte) bool {
if len(raw) < 24 {
return false
}
if !bytes.Equal(raw[4:8], []byte{0x6A, 0x50, 0x20, 0x20}) &&
!bytes.Equal(raw[4:8], []byte{0x6A, 0x50, 0x32, 0x20}) {
return false
}
return bytes.Equal(raw[20:24], sig)
}
// Webp matches a WebP file.
@@ -108,3 +169,20 @@ func Jxl(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte{0xFF, 0x0A}) ||
bytes.HasPrefix(raw, []byte("\x00\x00\x00\x0cJXL\x20\x0d\x0a\x87\x0a"))
}
// DXF matches Drawing Exchange Format AutoCAD file.
// There does not seem to be a clear specification and the files in the wild
// differ wildly.
// https://images.autodesk.com/adsk/files/autocad_2012_pdf_dxf-reference_enu.pdf
//
// I collected these signatures by downloading a few dozen files from
// http://cd.textfiles.com/amigaenv/DXF/OBJEKTE/ and
// https://sembiance.com/fileFormatSamples/poly/dxf/ and then
// xxd -l 16 {} | sort | uniq.
// These signatures are only for the ASCII version of DXF. There is a binary version too.
func DXF(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte(" 0\x0ASECTION\x0A")) ||
bytes.HasPrefix(raw, []byte(" 0\x0D\x0ASECTION\x0D\x0A")) ||
bytes.HasPrefix(raw, []byte("0\x0ASECTION\x0A")) ||
bytes.HasPrefix(raw, []byte("0\x0D\x0ASECTION\x0D\x0A"))
}

View File

@@ -3,7 +3,6 @@ package magic
import (
"bytes"
"fmt"
"github.com/gabriel-vasile/mimetype/internal/scan"
)
@@ -22,37 +21,20 @@ type (
}
)
// prefix creates a Detector which returns true if any of the provided signatures
// is the prefix of the raw input.
func prefix(sigs ...[]byte) Detector {
return func(raw []byte, limit uint32) bool {
for _, s := range sigs {
if bytes.HasPrefix(raw, s) {
return true
}
}
return false
}
}
// offset creates a Detector which returns true if the provided signature can be
// offset returns true if the provided signature can be
// found at offset in the raw input.
func offset(sig []byte, offset int) Detector {
return func(raw []byte, limit uint32) bool {
return len(raw) > offset && bytes.HasPrefix(raw[offset:], sig)
}
func offset(raw []byte, sig []byte, offset int) bool {
return len(raw) > offset && bytes.HasPrefix(raw[offset:], sig)
}
// ciPrefix is like prefix but the check is case insensitive.
func ciPrefix(sigs ...[]byte) Detector {
return func(raw []byte, limit uint32) bool {
for _, s := range sigs {
if ciCheck(s, raw) {
return true
}
func ciPrefix(raw []byte, sigs ...[]byte) bool {
for _, s := range sigs {
if ciCheck(s, raw) {
return true
}
return false
}
return false
}
func ciCheck(sig, raw []byte) bool {
if len(raw) < len(sig)+1 {
@@ -72,22 +54,18 @@ func ciCheck(sig, raw []byte) bool {
return true
}
// xml creates a Detector which returns true if any of the provided XML signatures
// matches the raw input.
func xml(sigs ...xmlSig) Detector {
return func(raw []byte, limit uint32) bool {
b := scan.Bytes(raw)
b.TrimLWS()
if len(b) == 0 {
return false
}
for _, s := range sigs {
if xmlCheck(s, b) {
return true
}
}
// xml returns true if any of the provided XML signatures matches the raw input.
func xml(b scan.Bytes, sigs ...xmlSig) bool {
b.TrimLWS()
if len(b) == 0 {
return false
}
for _, s := range sigs {
if xmlCheck(s, b) {
return true
}
}
return false
}
func xmlCheck(sig xmlSig, raw []byte) bool {
raw = raw[:min(len(raw), 512)]
@@ -103,28 +81,24 @@ func xmlCheck(sig xmlSig, raw []byte) bool {
return localNameIndex != -1 && localNameIndex < bytes.Index(raw, sig.xmlns)
}
// markup creates a Detector which returns true is any of the HTML signatures
// matches the raw input.
func markup(sigs ...[]byte) Detector {
return func(raw []byte, limit uint32) bool {
b := scan.Bytes(raw)
if bytes.HasPrefix(b, []byte{0xEF, 0xBB, 0xBF}) {
// We skip the UTF-8 BOM if present to ensure we correctly
// process any leading whitespace. The presence of the BOM
// is taken into account during charset detection in charset.go.
b.Advance(3)
}
b.TrimLWS()
if len(b) == 0 {
return false
}
for _, s := range sigs {
if markupCheck(s, b) {
return true
}
}
// markup returns true is any of the HTML signatures matches the raw input.
func markup(b scan.Bytes, sigs ...[]byte) bool {
if bytes.HasPrefix(b, []byte{0xEF, 0xBB, 0xBF}) {
// We skip the UTF-8 BOM if present to ensure we correctly
// process any leading whitespace. The presence of the BOM
// is taken into account during charset detection in charset.go.
b.Advance(3)
}
b.TrimLWS()
if len(b) == 0 {
return false
}
for _, s := range sigs {
if markupCheck(s, b) {
return true
}
}
return false
}
func markupCheck(sig, raw []byte) bool {
if len(raw) < len(sig)+1 {
@@ -149,29 +123,17 @@ func markupCheck(sig, raw []byte) bool {
return true
}
// ftyp creates a Detector which returns true if any of the FTYP signatures
// matches the raw input.
func ftyp(sigs ...[]byte) Detector {
return func(raw []byte, limit uint32) bool {
if len(raw) < 12 {
return false
}
for _, s := range sigs {
if bytes.Equal(raw[8:12], s) {
return true
}
}
// ftyp returns true if any of the FTYP signatures matches the raw input.
func ftyp(raw []byte, sigs ...[]byte) bool {
if len(raw) < 12 {
return false
}
}
func newXMLSig(localName, xmlns string) xmlSig {
ret := xmlSig{xmlns: []byte(xmlns)}
if localName != "" {
ret.localName = []byte(fmt.Sprintf("<%s", localName))
for _, s := range sigs {
if bytes.Equal(raw[8:12], s) {
return true
}
}
return ret
return false
}
// A valid shebang starts with the "#!" characters,
@@ -184,29 +146,17 @@ func newXMLSig(localName, xmlns string) xmlSig {
// #! /usr/bin/env php
//
// /usr/bin/env is the interpreter, php is the first and only argument.
func shebang(sigs ...[]byte) Detector {
return func(raw []byte, limit uint32) bool {
b := scan.Bytes(raw)
line := b.Line()
for _, s := range sigs {
if shebangCheck(s, line) {
return true
}
func shebang(b scan.Bytes, matchFlags scan.Flags, sigs ...[]byte) bool {
line := b.Line()
if len(line) < 2 || line[0] != '#' || line[1] != '!' {
return false
}
line = line[2:]
line.TrimLWS()
for _, s := range sigs {
if line.Match(s, matchFlags) != -1 {
return true
}
return false
}
}
func shebangCheck(sig []byte, raw scan.Bytes) bool {
if len(raw) < len(sig)+2 {
return false
}
if raw[0] != '#' || raw[1] != '!' {
return false
}
raw.Advance(2) // skip #! we checked above
raw.TrimLWS()
raw.TrimRWS()
return bytes.Equal(raw, sig)
return false
}

View File

@@ -0,0 +1,12 @@
package magic
import "bytes"
// GRIB matches a GRIdded Binary meteorological file.
// https://www.nco.ncep.noaa.gov/pmb/docs/on388/
// https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/
func GRIB(raw []byte, _ uint32) bool {
return len(raw) > 7 &&
bytes.HasPrefix(raw, []byte("GRIB")) &&
(raw[7] == 1 || raw[7] == 2)
}

View File

@@ -44,17 +44,6 @@ func Ole(raw []byte, limit uint32) bool {
return bytes.HasPrefix(raw, []byte{0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1})
}
// Aaf matches an Advanced Authoring Format file.
// See: https://pyaaf.readthedocs.io/en/latest/about.html
// See: https://en.wikipedia.org/wiki/Advanced_Authoring_Format
func Aaf(raw []byte, limit uint32) bool {
if len(raw) < 31 {
return false
}
return bytes.HasPrefix(raw[8:], []byte{0x41, 0x41, 0x46, 0x42, 0x0D, 0x00, 0x4F, 0x4D}) &&
(raw[30] == 0x09 || raw[30] == 0x0C)
}
// Doc matches a Microsoft Word 97-2003 file.
// See: https://github.com/decalage2/oletools/blob/412ee36ae45e70f42123e835871bac956d958461/oletools/common/clsid.py
func Doc(raw []byte, _ uint32) bool {
@@ -203,9 +192,21 @@ func matchOleClsid(in []byte, clsid []byte) bool {
// Expected offset of CLSID for root storage object.
clsidOffset := sectorLength*(1+firstSecID) + 80
if len(in) <= clsidOffset+16 {
// #731 offset is outside in or wrapped around due to integer overflow.
if len(in) <= clsidOffset+16 || clsidOffset < 0 {
return false
}
return bytes.HasPrefix(in[clsidOffset:], clsid)
}
// WPD matches a WordPerfect document.
func WPD(raw []byte, _ uint32) bool {
if len(raw) < 10 {
return false
}
if !bytes.HasPrefix(raw, []byte("\xffWPC")) {
return false
}
return raw[8] == 1 && raw[9] == 10
}

View File

@@ -10,9 +10,9 @@ import (
"github.com/gabriel-vasile/mimetype/internal/scan"
)
var (
// HTML matches a Hypertext Markup Language file.
HTML = markup(
// HTML matches a Hypertext Markup Language file.
func HTML(raw []byte, _ uint32) bool {
return markup(raw,
[]byte("<!DOCTYPE HTML"),
[]byte("<HTML"),
[]byte("<HEAD"),
@@ -31,138 +31,259 @@ var (
[]byte("<P"),
[]byte("<!--"),
)
// XML matches an Extensible Markup Language file.
XML = markup([]byte("<?XML"))
// Owl2 matches an Owl ontology file.
Owl2 = xml(newXMLSig("Ontology", `xmlns="http://www.w3.org/2002/07/owl#"`))
// Rss matches a Rich Site Summary file.
Rss = xml(newXMLSig("rss", ""))
// Atom matches an Atom Syndication Format file.
Atom = xml(newXMLSig("feed", `xmlns="http://www.w3.org/2005/Atom"`))
// Kml matches a Keyhole Markup Language file.
Kml = xml(
newXMLSig("kml", `xmlns="http://www.opengis.net/kml/2.2"`),
newXMLSig("kml", `xmlns="http://earth.google.com/kml/2.0"`),
newXMLSig("kml", `xmlns="http://earth.google.com/kml/2.1"`),
newXMLSig("kml", `xmlns="http://earth.google.com/kml/2.2"`),
}
// XML matches an Extensible Markup Language file.
func XML(raw []byte, _ uint32) bool {
return markup(raw, []byte("<?XML"))
}
// Owl2 matches an Owl ontology file.
func Owl2(raw []byte, _ uint32) bool {
return xml(raw,
xmlSig{[]byte("<Ontology"), []byte(`xmlns="http://www.w3.org/2002/07/owl#"`)},
)
// Xliff matches a XML Localization Interchange File Format file.
Xliff = xml(newXMLSig("xliff", `xmlns="urn:oasis:names:tc:xliff:document:1.2"`))
// Collada matches a COLLAborative Design Activity file.
Collada = xml(newXMLSig("COLLADA", `xmlns="http://www.collada.org/2005/11/COLLADASchema"`))
// Gml matches a Geography Markup Language file.
Gml = xml(
newXMLSig("", `xmlns:gml="http://www.opengis.net/gml"`),
newXMLSig("", `xmlns:gml="http://www.opengis.net/gml/3.2"`),
newXMLSig("", `xmlns:gml="http://www.opengis.net/gml/3.3/exr"`),
}
// Rss matches a Rich Site Summary file.
func Rss(raw []byte, _ uint32) bool {
return xml(raw,
xmlSig{[]byte("<rss"), []byte{}},
)
// Gpx matches a GPS Exchange Format file.
Gpx = xml(newXMLSig("gpx", `xmlns="http://www.topografix.com/GPX/1/1"`))
// Tcx matches a Training Center XML file.
Tcx = xml(newXMLSig("TrainingCenterDatabase", `xmlns="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2"`))
// X3d matches an Extensible 3D Graphics file.
X3d = xml(newXMLSig("X3D", `xmlns:xsd="http://www.w3.org/2001/XMLSchema-instance"`))
// Amf matches an Additive Manufacturing XML file.
Amf = xml(newXMLSig("amf", ""))
// Threemf matches a 3D Manufacturing Format file.
Threemf = xml(newXMLSig("model", `xmlns="http://schemas.microsoft.com/3dmanufacturing/core/2015/02"`))
// Xfdf matches a XML Forms Data Format file.
Xfdf = xml(newXMLSig("xfdf", `xmlns="http://ns.adobe.com/xfdf/"`))
// VCard matches a Virtual Contact File.
VCard = ciPrefix([]byte("BEGIN:VCARD\n"), []byte("BEGIN:VCARD\r\n"))
// ICalendar matches a iCalendar file.
ICalendar = ciPrefix([]byte("BEGIN:VCALENDAR\n"), []byte("BEGIN:VCALENDAR\r\n"))
phpPageF = ciPrefix(
}
// Atom matches an Atom Syndication Format file.
func Atom(raw []byte, _ uint32) bool {
return xml(raw,
xmlSig{[]byte("<feed"), []byte(`xmlns="http://www.w3.org/2005/Atom"`)},
)
}
// Kml matches a Keyhole Markup Language file.
func Kml(raw []byte, _ uint32) bool {
return xml(raw,
xmlSig{[]byte("<kml"), []byte(`xmlns="http://www.opengis.net/kml/2.2"`)},
xmlSig{[]byte("<kml"), []byte(`xmlns="http://earth.google.com/kml/2.0"`)},
xmlSig{[]byte("<kml"), []byte(`xmlns="http://earth.google.com/kml/2.1"`)},
xmlSig{[]byte("<kml"), []byte(`xmlns="http://earth.google.com/kml/2.2"`)},
)
}
// Xliff matches a XML Localization Interchange File Format file.
func Xliff(raw []byte, _ uint32) bool {
return xml(raw,
xmlSig{[]byte("<xliff"), []byte(`xmlns="urn:oasis:names:tc:xliff:document:1.2"`)},
)
}
// Collada matches a COLLAborative Design Activity file.
func Collada(raw []byte, _ uint32) bool {
return xml(raw,
xmlSig{[]byte("<COLLADA"), []byte(`xmlns="http://www.collada.org/2005/11/COLLADASchema"`)},
)
}
// Gml matches a Geography Markup Language file.
func Gml(raw []byte, _ uint32) bool {
return xml(raw,
xmlSig{[]byte{}, []byte(`xmlns:gml="http://www.opengis.net/gml"`)},
xmlSig{[]byte{}, []byte(`xmlns:gml="http://www.opengis.net/gml/3.2"`)},
xmlSig{[]byte{}, []byte(`xmlns:gml="http://www.opengis.net/gml/3.3/exr"`)},
)
}
// Gpx matches a GPS Exchange Format file.
func Gpx(raw []byte, _ uint32) bool {
return xml(raw,
xmlSig{[]byte("<gpx"), []byte(`xmlns="http://www.topografix.com/GPX/1/1"`)},
)
}
// Tcx matches a Training Center XML file.
func Tcx(raw []byte, _ uint32) bool {
return xml(raw,
xmlSig{[]byte("<TrainingCenterDatabase"), []byte(`xmlns="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2"`)},
)
}
// X3d matches an Extensible 3D Graphics file.
func X3d(raw []byte, _ uint32) bool {
return xml(raw,
xmlSig{[]byte("<X3D"), []byte(`xmlns:xsd="http://www.w3.org/2001/XMLSchema-instance"`)},
)
}
// Amf matches an Additive Manufacturing XML file.
func Amf(raw []byte, _ uint32) bool {
return xml(raw, xmlSig{[]byte("<amf"), []byte{}})
}
// Threemf matches a 3D Manufacturing Format file.
func Threemf(raw []byte, _ uint32) bool {
return xml(raw,
xmlSig{[]byte("<model"), []byte(`xmlns="http://schemas.microsoft.com/3dmanufacturing/core/2015/02"`)},
)
}
// Xfdf matches a XML Forms Data Format file.
func Xfdf(raw []byte, _ uint32) bool {
return xml(raw, xmlSig{[]byte("<xfdf"), []byte(`xmlns="http://ns.adobe.com/xfdf/"`)})
}
// VCard matches a Virtual Contact File.
func VCard(raw []byte, _ uint32) bool {
return ciPrefix(raw, []byte("BEGIN:VCARD\n"), []byte("BEGIN:VCARD\r\n"))
}
// ICalendar matches a iCalendar file.
func ICalendar(raw []byte, _ uint32) bool {
return ciPrefix(raw, []byte("BEGIN:VCALENDAR\n"), []byte("BEGIN:VCALENDAR\r\n"))
}
func phpPageF(raw []byte, _ uint32) bool {
return ciPrefix(raw,
[]byte("<?PHP"),
[]byte("<?\n"),
[]byte("<?\r"),
[]byte("<? "),
)
phpScriptF = shebang(
}
func phpScriptF(raw []byte, _ uint32) bool {
return shebang(raw,
scan.CompactWS,
[]byte("/usr/local/bin/php"),
[]byte("/usr/bin/php"),
[]byte("/usr/bin/env php"),
[]byte("/usr/bin/env -S php"),
)
// Js matches a Javascript file.
Js = shebang(
}
// Js matches a Javascript file.
func Js(raw []byte, _ uint32) bool {
return shebang(raw,
scan.CompactWS,
[]byte("/bin/node"),
[]byte("/usr/bin/node"),
[]byte("/bin/nodejs"),
[]byte("/usr/bin/nodejs"),
[]byte("/usr/bin/env node"),
[]byte("/usr/bin/env -S node"),
[]byte("/usr/bin/env nodejs"),
[]byte("/usr/bin/env -S nodejs"),
)
// Lua matches a Lua programming language file.
Lua = shebang(
}
// Lua matches a Lua programming language file.
func Lua(raw []byte, _ uint32) bool {
return shebang(raw,
scan.CompactWS|scan.FullWord,
[]byte("/usr/bin/lua"),
[]byte("/usr/local/bin/lua"),
[]byte("/usr/bin/env lua"),
[]byte("/usr/bin/env -S lua"),
)
// Perl matches a Perl programming language file.
Perl = shebang(
}
// Perl matches a Perl programming language file.
func Perl(raw []byte, _ uint32) bool {
return shebang(raw,
scan.CompactWS|scan.FullWord,
[]byte("/usr/bin/perl"),
[]byte("/usr/bin/env perl"),
[]byte("/usr/bin/env -S perl"),
)
// Python matches a Python programming language file.
Python = shebang(
}
// Python matches a Python programming language file.
func Python(raw []byte, _ uint32) bool {
return shebang(raw,
scan.CompactWS,
[]byte("/usr/bin/python"),
[]byte("/usr/local/bin/python"),
[]byte("/usr/bin/env python"),
[]byte("/usr/bin/env -S python"),
[]byte("/usr/bin/python2"),
[]byte("/usr/local/bin/python2"),
[]byte("/usr/bin/env python2"),
[]byte("/usr/bin/env -S python2"),
[]byte("/usr/bin/python3"),
[]byte("/usr/local/bin/python3"),
[]byte("/usr/bin/env python3"),
[]byte("/usr/bin/env -S python3"),
)
// Ruby matches a Ruby programming language file.
Ruby = shebang(
}
// Ruby matches a Ruby programming language file.
func Ruby(raw []byte, _ uint32) bool {
return shebang(raw,
scan.CompactWS,
[]byte("/usr/bin/ruby"),
[]byte("/usr/local/bin/ruby"),
[]byte("/usr/bin/env ruby"),
[]byte("/usr/bin/env -S ruby"),
)
// Tcl matches a Tcl programming language file.
Tcl = shebang(
}
// Tcl matches a Tcl programming language file.
func Tcl(raw []byte, _ uint32) bool {
return shebang(raw,
scan.CompactWS,
[]byte("/usr/bin/tcl"),
[]byte("/usr/local/bin/tcl"),
[]byte("/usr/bin/env tcl"),
[]byte("/usr/bin/env -S tcl"),
[]byte("/usr/bin/tclsh"),
[]byte("/usr/local/bin/tclsh"),
[]byte("/usr/bin/env tclsh"),
[]byte("/usr/bin/env -S tclsh"),
[]byte("/usr/bin/wish"),
[]byte("/usr/local/bin/wish"),
[]byte("/usr/bin/env wish"),
[]byte("/usr/bin/env -S wish"),
)
// Rtf matches a Rich Text Format file.
Rtf = prefix([]byte("{\\rtf"))
// Shell matches a shell script file.
Shell = shebang(
}
// Rtf matches a Rich Text Format file.
func Rtf(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte("{\\rtf"))
}
// Shell matches a shell script file.
func Shell(raw []byte, _ uint32) bool {
return shebang(raw,
scan.CompactWS|scan.FullWord,
[]byte("/bin/sh"),
[]byte("/bin/bash"),
[]byte("/usr/local/bin/bash"),
[]byte("/usr/bin/env bash"),
[]byte("/usr/bin/env -S bash"),
[]byte("/bin/csh"),
[]byte("/usr/local/bin/csh"),
[]byte("/usr/bin/env csh"),
[]byte("/usr/bin/env -S csh"),
[]byte("/bin/dash"),
[]byte("/usr/local/bin/dash"),
[]byte("/usr/bin/env dash"),
[]byte("/usr/bin/env -S dash"),
[]byte("/bin/ksh"),
[]byte("/usr/local/bin/ksh"),
[]byte("/usr/bin/env ksh"),
[]byte("/usr/bin/env -S ksh"),
[]byte("/bin/tcsh"),
[]byte("/usr/local/bin/tcsh"),
[]byte("/usr/bin/env tcsh"),
[]byte("/usr/bin/env -S tcsh"),
[]byte("/bin/zsh"),
[]byte("/usr/local/bin/zsh"),
[]byte("/usr/bin/env zsh"),
[]byte("/usr/bin/env -S zsh"),
)
)
}
// Text matches a plain text file.
//
// TODO: This function does not parse BOM-less UTF16 and UTF32 files. Not really
// sure it should. Linux file utility also requires a BOM for UTF16 and UTF32.
// sure it should. libmagic also requires a BOM for UTF16 and UTF32.
func Text(raw []byte, _ uint32) bool {
// First look for BOM.
if cset := charset.FromBOM(raw); cset != "" {
@@ -183,10 +304,14 @@ func Text(raw []byte, _ uint32) bool {
// XHTML matches an XHTML file. This check depends on the XML check to have passed.
func XHTML(raw []byte, limit uint32) bool {
raw = raw[:min(len(raw), 4096)]
raw = raw[:min(len(raw), 1024)]
b := scan.Bytes(raw)
return b.Search([]byte("<!DOCTYPE HTML"), scan.CompactWS|scan.IgnoreCase) != -1 ||
b.Search([]byte("<HTML XMLNS="), scan.CompactWS|scan.IgnoreCase) != -1
i, _ := b.Search([]byte("<!DOCTYPE HTML"), scan.CompactWS|scan.IgnoreCase)
if i != -1 {
return true
}
i, _ = b.Search([]byte("<HTML XMLNS="), scan.CompactWS|scan.IgnoreCase)
return i != -1
}
// Php matches a PHP: Hypertext Preprocessor file.
@@ -227,13 +352,20 @@ func GLTF(raw []byte, limit uint32) bool {
return jsonHelper(raw, limit, json.QueryGLTF, json.TokObject)
}
func jsonHelper(raw []byte, limit uint32, q string, wantTok int) bool {
if !json.LooksLikeObjectOrArray(raw) {
func jsonHelper(raw scan.Bytes, limit uint32, q string, wantToks ...int) bool {
firstNonWS := raw.FirstNonWS()
hasTargetTok := false
for _, t := range wantToks {
hasTargetTok = hasTargetTok || (t&json.TokArray > 0 && firstNonWS == '[')
hasTargetTok = hasTargetTok || (t&json.TokObject > 0 && firstNonWS == '{')
}
if !hasTargetTok {
return false
}
lraw := len(raw)
parsed, inspected, firstToken, querySatisfied := json.Parse(q, raw)
if !querySatisfied || firstToken&wantTok == 0 {
parsed, inspected, _, querySatisfied := json.Parse(q, raw)
if !querySatisfied {
return false
}
@@ -244,7 +376,7 @@ func jsonHelper(raw []byte, limit uint32, q string, wantTok int) bool {
// If a section of the file was provided, check if all of it was inspected.
// In other words, check that if there was a problem parsing, that problem
// occured at the last byte in the input.
// occurred at the last byte in the input.
return inspected == lraw && lraw > 0
}
@@ -294,11 +426,12 @@ func svgWithoutXMLDeclaration(s scan.Bytes) bool {
return false
}
targetName, targetVal := "xmlns", "http://www.w3.org/2000/svg"
aName, aVal, hasMore := "", "", true
targetName, targetVal := []byte("xmlns"), []byte("http://www.w3.org/2000/svg")
var aName, aVal []byte
hasMore := true
for hasMore {
aName, aVal, hasMore = mkup.GetAnAttribute(&s)
if aName == targetName && aVal == targetVal {
if bytes.Equal(aName, targetName) && bytes.Equal(aVal, targetVal) {
return true
}
if !hasMore {
@@ -325,10 +458,11 @@ func svgWithXMLDeclaration(s scan.Bytes) bool {
// version is a required attribute for XML.
hasVersion := false
aName, hasMore := "", true
var aName []byte
hasMore := true
for hasMore {
aName, _, hasMore = mkup.GetAnAttribute(&s)
if aName == "version" {
if bytes.Equal(aName, []byte("version")) {
hasVersion = true
break
}
@@ -409,3 +543,57 @@ func Vtt(raw []byte, limit uint32) bool {
return bytes.Equal(raw, []byte{0xEF, 0xBB, 0xBF, 0x57, 0x45, 0x42, 0x56, 0x54, 0x54}) || // UTF-8 BOM and "WEBVTT"
bytes.Equal(raw, []byte{0x57, 0x45, 0x42, 0x56, 0x54, 0x54}) // "WEBVTT"
}
type rfc822Hint struct {
h []byte
matchFlags scan.Flags
}
// The hints come from libmagic, but the implementation is bit different. libmagic
// only checks if the file starts with the hint, while we additionally look for
// a secondary hint in the first few lines of input.
func RFC822(raw []byte, limit uint32) bool {
b := scan.Bytes(raw)
// Keep hints here to avoid instantiating them several times in lineHasRFC822Hint.
// The alternative is to make them a package level var, but then they'd go
// on the heap.
// Some of the hints are IgnoreCase, some not. I selected based on what libmagic
// does and based on personal observations from sample files.
hints := []rfc822Hint{
{[]byte("From: "), 0},
{[]byte("To: "), 0},
{[]byte("CC: "), scan.IgnoreCase},
{[]byte("Date: "), 0},
{[]byte("Subject: "), 0},
{[]byte("Received: "), 0},
{[]byte("Relay-Version: "), 0},
{[]byte("#! rnews"), 0},
{[]byte("N#! rnews"), 0},
{[]byte("Forward to"), 0},
{[]byte("Pipe to"), 0},
{[]byte("DELIVERED-TO: "), scan.IgnoreCase},
{[]byte("RETURN-PATH: "), scan.IgnoreCase},
{[]byte("Content-Type: "), 0},
{[]byte("Content-Transfer-Encoding: "), 0},
}
if !lineHasRFC822Hint(b.Line(), hints) {
return false
}
for i := 0; i < 20; i++ {
if lineHasRFC822Hint(b.Line(), hints) {
return true
}
}
return false
}
func lineHasRFC822Hint(b scan.Bytes, hints []rfc822Hint) bool {
for _, h := range hints {
if b.Match(h.h, h.matchFlags) > -1 {
return true
}
}
return false
}

View File

@@ -4,17 +4,23 @@ import (
"bytes"
)
var (
// Flv matches a Flash video file.
Flv = prefix([]byte("\x46\x4C\x56\x01"))
// Asf matches an Advanced Systems Format file.
Asf = prefix([]byte{
// Flv matches a Flash video file.
func Flv(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte("\x46\x4C\x56\x01"))
}
// Asf matches an Advanced Systems Format file.
func Asf(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte{
0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11,
0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C,
})
// Rmvb matches a RealMedia Variable Bitrate file.
Rmvb = prefix([]byte{0x2E, 0x52, 0x4D, 0x46})
)
}
// Rmvb matches a RealMedia Variable Bitrate file.
func Rmvb(raw []byte, _ uint32) bool {
return bytes.HasPrefix(raw, []byte{0x2E, 0x52, 0x4D, 0x46})
}
// WebM matches a WebM file.
func WebM(raw []byte, limit uint32) bool {
@@ -63,9 +69,9 @@ func isFileTypeNamePresent(in []byte, flType string) bool {
// vintWidth parses the variable-integer width in matroska containers
func vintWidth(v int) int {
mask, max, num := 128, 8, 1
for num < max && v&mask == 0 {
mask = mask >> 1
mask, nTimes, num := 128, 8, 1
for num < nTimes && v&mask == 0 {
mask >>= 1
num++
}
return num

View File

@@ -6,32 +6,65 @@ import (
"github.com/gabriel-vasile/mimetype/internal/scan"
)
var (
// Odt matches an OpenDocument Text file.
Odt = offset([]byte("mimetypeapplication/vnd.oasis.opendocument.text"), 30)
// Ott matches an OpenDocument Text Template file.
Ott = offset([]byte("mimetypeapplication/vnd.oasis.opendocument.text-template"), 30)
// Ods matches an OpenDocument Spreadsheet file.
Ods = offset([]byte("mimetypeapplication/vnd.oasis.opendocument.spreadsheet"), 30)
// Ots matches an OpenDocument Spreadsheet Template file.
Ots = offset([]byte("mimetypeapplication/vnd.oasis.opendocument.spreadsheet-template"), 30)
// Odp matches an OpenDocument Presentation file.
Odp = offset([]byte("mimetypeapplication/vnd.oasis.opendocument.presentation"), 30)
// Otp matches an OpenDocument Presentation Template file.
Otp = offset([]byte("mimetypeapplication/vnd.oasis.opendocument.presentation-template"), 30)
// Odg matches an OpenDocument Drawing file.
Odg = offset([]byte("mimetypeapplication/vnd.oasis.opendocument.graphics"), 30)
// Otg matches an OpenDocument Drawing Template file.
Otg = offset([]byte("mimetypeapplication/vnd.oasis.opendocument.graphics-template"), 30)
// Odf matches an OpenDocument Formula file.
Odf = offset([]byte("mimetypeapplication/vnd.oasis.opendocument.formula"), 30)
// Odc matches an OpenDocument Chart file.
Odc = offset([]byte("mimetypeapplication/vnd.oasis.opendocument.chart"), 30)
// Epub matches an EPUB file.
Epub = offset([]byte("mimetypeapplication/epub+zip"), 30)
// Sxc matches an OpenOffice Spreadsheet file.
Sxc = offset([]byte("mimetypeapplication/vnd.sun.xml.calc"), 30)
)
// Odt matches an OpenDocument Text file.
func Odt(raw []byte, _ uint32) bool {
return offset(raw, []byte("mimetypeapplication/vnd.oasis.opendocument.text"), 30)
}
// Ott matches an OpenDocument Text Template file.
func Ott(raw []byte, _ uint32) bool {
return offset(raw, []byte("mimetypeapplication/vnd.oasis.opendocument.text-template"), 30)
}
// Ods matches an OpenDocument Spreadsheet file.
func Ods(raw []byte, _ uint32) bool {
return offset(raw, []byte("mimetypeapplication/vnd.oasis.opendocument.spreadsheet"), 30)
}
// Ots matches an OpenDocument Spreadsheet Template file.
func Ots(raw []byte, _ uint32) bool {
return offset(raw, []byte("mimetypeapplication/vnd.oasis.opendocument.spreadsheet-template"), 30)
}
// Odp matches an OpenDocument Presentation file.
func Odp(raw []byte, _ uint32) bool {
return offset(raw, []byte("mimetypeapplication/vnd.oasis.opendocument.presentation"), 30)
}
// Otp matches an OpenDocument Presentation Template file.
func Otp(raw []byte, _ uint32) bool {
return offset(raw, []byte("mimetypeapplication/vnd.oasis.opendocument.presentation-template"), 30)
}
// Odg matches an OpenDocument Drawing file.
func Odg(raw []byte, _ uint32) bool {
return offset(raw, []byte("mimetypeapplication/vnd.oasis.opendocument.graphics"), 30)
}
// Otg matches an OpenDocument Drawing Template file.
func Otg(raw []byte, _ uint32) bool {
return offset(raw, []byte("mimetypeapplication/vnd.oasis.opendocument.graphics-template"), 30)
}
// Odf matches an OpenDocument Formula file.
func Odf(raw []byte, _ uint32) bool {
return offset(raw, []byte("mimetypeapplication/vnd.oasis.opendocument.formula"), 30)
}
// Odc matches an OpenDocument Chart file.
func Odc(raw []byte, _ uint32) bool {
return offset(raw, []byte("mimetypeapplication/vnd.oasis.opendocument.chart"), 30)
}
// Epub matches an EPUB file.
func Epub(raw []byte, _ uint32) bool {
return offset(raw, []byte("mimetypeapplication/epub+zip"), 30)
}
// Sxc matches an OpenOffice Spreadsheet file.
func Sxc(raw []byte, _ uint32) bool {
return offset(raw, []byte("mimetypeapplication/vnd.sun.xml.calc"), 30)
}
// Zip matches a zip archive.
func Zip(raw []byte, limit uint32) bool {
@@ -52,10 +85,14 @@ func Zip(raw []byte, limit uint32) bool {
// (instead of relying on offsets told by the file.)
func Jar(raw []byte, limit uint32) bool {
return executableJar(raw) ||
// First entry must be an empty META-INF directory or the manifest.
// There is no specification saying that, but the jar reader and writer
// implementations from Java do it that way.
// https://github.com/openjdk/jdk/blob/88c4678eed818cbe9380f35352e90883fed27d33/src/java.base/share/classes/java/util/jar/JarInputStream.java#L170-L173
zipHas(raw, zipEntries{{
name: []byte("META-INF/MANIFEST.MF"),
}, {
name: []byte("META-INF/"),
}, {
name: []byte("META-INF/MANIFEST.MF"),
}}, 1)
}
@@ -94,11 +131,14 @@ type zipEntries []struct {
func (z zipEntries) match(file []byte) bool {
for i := range z {
if z[i].dir && bytes.HasPrefix(file, z[i].name) {
return true
}
if bytes.Equal(file, z[i].name) {
return true
if z[i].dir {
if bytes.HasPrefix(file, z[i].name) {
return true
}
} else {
if bytes.Equal(file, z[i].name) {
return true
}
}
}
return false
@@ -134,11 +174,11 @@ func msoxml(raw scan.Bytes, searchFor zipEntries, stopAfter int) bool {
// If the first is not one of the next usually expected entries,
// then abort this check.
if i == 0 {
if !bytes.Equal(f, []byte("[Content_Types].xml")) &&
!bytes.Equal(f, []byte("_rels/.rels")) &&
!bytes.Equal(f, []byte("docProps")) &&
!bytes.Equal(f, []byte("customXml")) &&
!bytes.Equal(f, []byte("[trash]")) {
if !bytes.Equal(f, []byte("[Content_Types].xml")) && // this is a file
!bytes.HasPrefix(f, []byte("_rels/")) && // these are directories
!bytes.HasPrefix(f, []byte("docProps/")) &&
!bytes.HasPrefix(f, []byte("customXml/")) &&
!bytes.HasPrefix(f, []byte("[trash]/")) {
return false
}
}

View File

@@ -8,46 +8,48 @@ import (
"github.com/gabriel-vasile/mimetype/internal/scan"
)
func GetAnAttribute(s *scan.Bytes) (name, val string, hasMore bool) {
// GetAnAttribute assumes we passed over an SGML tag and extracts first
// attribute and its value.
//
// Initially, this code existed inside charset/charset.go, because it was part of
// implementing the https://html.spec.whatwg.org/multipage/parsing.html#prescan-a-byte-stream-to-determine-its-encoding
// algorithm. But because extracting an attribute from a tag is the same for
// both HTML and XML, then the code was moved here.
func GetAnAttribute(s *scan.Bytes) (name, val []byte, hasMore bool) {
for scan.ByteIsWS(s.Peek()) || s.Peek() == '/' {
s.Advance(1)
}
if s.Peek() == '>' {
return "", "", false
return nil, nil, false
}
// Allocate 10 to avoid resizes.
// Attribute names and values are continuous slices of bytes in input,
// so we could do without allocating and returning slices of input.
nameB := make([]byte, 0, 10)
origS, end := *s, 0
// step 4 and 5
for {
// bap means byte at position in the specification.
bap := s.Pop()
if bap == 0 {
return "", "", false
return nil, nil, false
}
if bap == '=' && len(nameB) > 0 {
if bap == '=' && end > 0 {
val, hasMore := getAValue(s)
return string(nameB), string(val), hasMore
return origS[:end], val, hasMore
} else if scan.ByteIsWS(bap) {
for scan.ByteIsWS(s.Peek()) {
s.Advance(1)
}
if s.Peek() != '=' {
return string(nameB), "", true
return origS[:end], nil, true
}
s.Advance(1)
for scan.ByteIsWS(s.Peek()) {
s.Advance(1)
}
val, hasMore := getAValue(s)
return string(nameB), string(val), hasMore
return origS[:end], val, hasMore
} else if bap == '/' || bap == '>' {
return string(nameB), "", false
} else if bap >= 'A' && bap <= 'Z' {
nameB = append(nameB, bap+0x20)
} else {
nameB = append(nameB, bap)
return origS[:end], nil, false
} else { // for any ASCII, non-ASCII, just advance
end++
}
}
}

View File

@@ -35,6 +35,19 @@ func (b *Bytes) TrimRWS() {
}
}
// FirstNonWS returns the first non-whitespace character from b,
// or 0x00 if no such character is found.
func (b Bytes) FirstNonWS() byte {
for i := range b {
if ByteIsWS(b[i]) {
continue
}
return b[i]
}
return 0x00
}
// Peek one byte from b or 0x00 if b is empty.
func (b *Bytes) Peek() byte {
if len(*b) > 0 {
@@ -63,8 +76,8 @@ func (b *Bytes) PopN(n int) []byte {
return nil
}
// PopUntil will advance b until, but not including, the first occurence of stopAt
// character. If no occurence is found, then it will advance until the end of b.
// PopUntil will advance b until, but not including, the first occurrence of stopAt
// character. If no occurrence is found, then it will advance until the end of b.
// The returned Bytes is a slice of all the bytes that we're advanced over.
func (b *Bytes) PopUntil(stopAt ...byte) Bytes {
if len(*b) == 0 {
@@ -77,7 +90,7 @@ func (b *Bytes) PopUntil(stopAt ...byte) Bytes {
prefix := (*b)[:i]
*b = (*b)[i:]
return Bytes(prefix)
return prefix
}
// ReadSlice is the same as PopUntil, but the returned value includes stopAt as well.
@@ -94,7 +107,7 @@ func (b *Bytes) ReadSlice(stopAt byte) Bytes {
prefix := (*b)[:i]
*b = (*b)[i:]
return Bytes(prefix)
return prefix
}
// Line returns the first line from b and advances b with the length of the
@@ -117,7 +130,7 @@ func (b *Bytes) Line() Bytes {
// If b length is less than readLimit, it means we received an incomplete file
// and proceed with dropping the last line.
func (b *Bytes) DropLastLine(readLimit uint32) {
if readLimit == 0 || uint32(len(*b)) < readLimit {
if readLimit == 0 || uint64(len(*b)) < uint64(readLimit) {
return
}
@@ -138,46 +151,85 @@ func (b *Bytes) Uint16() (uint16, bool) {
return v, true
}
type Flags int
const (
CompactWS = 1 << iota
// CompactWS will make one whitespace from pattern to match one or more spaces from input.
CompactWS Flags = 1 << iota
// IgnoreCase will match lower case from pattern with lower case from input.
// IgnoreCase will match upper case from pattern with both lower and upper case from input.
// This flag is not really well named,
IgnoreCase
// FullWord ensures the input ends with a full word (it's followed by spaces.)
FullWord
)
// Search for occurences of pattern p inside b at any index.
func (b Bytes) Search(p []byte, flags int) int {
// Search for occurrences of pattern p inside b at any index.
// It returns the index where p was found in b and how many bytes were needed
// for matching the pattern.
func (b Bytes) Search(p []byte, flags Flags) (i int, l int) {
lb, lp := len(b), len(p)
if lp == 0 {
return 0, 0
}
if lb == 0 {
return -1, 0
}
if flags == 0 {
return bytes.Index(b, p)
if i = bytes.Index(b, p); i == -1 {
return -1, 0
} else {
return i, lp
}
}
lb, lp := len(b), len(p)
for i := range b {
if lb-i < lp {
return -1
return -1, 0
}
if b[i:].Match(p, flags) {
return i
if l = b[i:].Match(p, flags); l != -1 {
return i, l
}
}
return 0
return -1, 0
}
// Match pattern p at index 0 of b.
func (b Bytes) Match(p []byte, flags int) bool {
// Match returns how many bytes were needed to match pattern p.
// It returns -1 if p does not match b.
func (b Bytes) Match(p []byte, flags Flags) int {
l := len(b)
if len(p) == 0 {
return 0
}
if l == 0 {
return -1
}
// If no flags, or scanning for full word at the end of pattern then
// do a fast HasPrefix check.
// For other flags it's not possible to use HasPrefix.
if flags == 0 || flags&FullWord > 0 {
if bytes.HasPrefix(b, p) {
b = b[len(p):]
p = p[len(p):]
goto out
}
return -1
}
for len(b) > 0 {
// If we finished all we we're looking for from p.
// If we finished all we were looking for from p.
if len(p) == 0 {
return true
goto out
}
if flags&IgnoreCase > 0 && isUpper(p[0]) {
if upper(b[0]) != p[0] {
return false
return -1
}
b, p = b[1:], p[1:]
} else if flags&CompactWS > 0 && ByteIsWS(p[0]) {
p = p[1:]
if !ByteIsWS(b[0]) {
return false
return -1
}
b = b[1:]
if !ByteIsWS(p[0]) {
@@ -185,12 +237,22 @@ func (b Bytes) Match(p []byte, flags int) bool {
}
} else {
if b[0] != p[0] {
return false
return -1
}
b, p = b[1:], p[1:]
}
}
return true
out:
// If p still has leftover characters, it means it didn't fully match b.
if len(p) > 0 {
return -1
}
if flags&FullWord > 0 {
if len(b) > 0 && !ByteIsWS(b[0]) {
return -1
}
}
return l - len(b)
}
func isUpper(c byte) bool {

View File

@@ -2,6 +2,8 @@ package mimetype
import (
"mime"
"slices"
"strings"
"github.com/gabriel-vasile/mimetype/internal/charset"
"github.com/gabriel-vasile/mimetype/internal/magic"
@@ -57,10 +59,8 @@ func (m *MIME) Is(expectedMIME string) bool {
return true
}
for _, alias := range m.aliases {
if alias == expectedMIME {
return true
}
if slices.Contains(m.aliases, expectedMIME) {
return true
}
return false
@@ -109,7 +109,7 @@ func (m *MIME) match(in []byte, readLimit uint32) *MIME {
// Limit the number of bytes searched for to 1024.
charset = f(in[:min(len(in), 1024)])
}
if m == root {
if m == root || charset == "" {
return m
}
@@ -126,6 +126,27 @@ func (m *MIME) flatten() []*MIME {
return out
}
// hierarchy returns an easy to read list of ancestors for m.
// For example, application/json would return json>txt>root.
func (m *MIME) hierarchy() string {
h := ""
for m := m; m != nil; m = m.Parent() {
e := strings.TrimPrefix(m.Extension(), ".")
if e == "" {
// There are some MIME without extensions. When generating the hierarchy,
// it would be confusing to use empty string as extension.
// Use the subtype instead; ex: application/x-executable -> x-executable.
e = strings.Split(m.String(), "/")[1]
if m.Is("application/octet-stream") {
// for octet-stream use root, because it's short and used in many places
e = "root"
}
}
h += ">" + e
}
return strings.TrimPrefix(h, ">")
}
// clone creates a new MIME with the provided optional MIME parameters.
func (m *MIME) clone(charset string) *MIME {
clonedMIME := m.mime
@@ -155,10 +176,11 @@ func (m *MIME) cloneHierarchy(charset string) *MIME {
}
func (m *MIME) lookup(mime string) *MIME {
for _, n := range append(m.aliases, m.mime) {
if n == mime {
return m
}
if mime == m.mime {
return m
}
if slices.Contains(m.aliases, mime) {
return m
}
for _, c := range m.children {

View File

@@ -12,7 +12,7 @@ import (
"sync/atomic"
)
var defaultLimit uint32 = 3072
const defaultLimit uint32 = 3072
// readLimit is the maximum number of bytes from the input used when detecting.
var readLimit uint32 = defaultLimit
@@ -112,15 +112,18 @@ func SetLimit(limit uint32) {
}
// Extend adds detection for other file formats.
// It is equivalent to calling Extend() on the root mime type "application/octet-stream".
// It is equivalent to calling Extend() on the root MIME type "application/octet-stream".
func Extend(detector func(raw []byte, limit uint32) bool, mime, extension string, aliases ...string) {
root.Extend(detector, mime, extension, aliases...)
}
// Lookup finds a MIME object by its string representation.
// The representation can be the main mime type, or any of its aliases.
func Lookup(mime string) *MIME {
// The representation can be the main MIME type, or any of its aliases.
func Lookup(m string) *MIME {
// We store the MIME types without optional params, so
// perform parsing to extract the target MIME type without optional params.
m, _, _ = mime.ParseMediaType(m)
mu.RLock()
defer mu.RUnlock()
return root.lookup(mime)
return root.lookup(m)
}

View File

@@ -1,196 +1,200 @@
## 191 Supported MIME types
## 195 Supported MIME types
This file is automatically generated when running tests. Do not edit manually.
Extension | MIME type | Aliases
--------- | --------- | -------
**n/a** | application/octet-stream | -
**.xpm** | image/x-xpixmap | -
**.7z** | application/x-7z-compressed | -
**.zip** | application/zip | application/x-zip, application/x-zip-compressed
**.docx** | application/vnd.openxmlformats-officedocument.wordprocessingml.document | -
**.pptx** | application/vnd.openxmlformats-officedocument.presentationml.presentation | -
**.xlsx** | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet | -
**.epub** | application/epub+zip | -
**.apk** | application/vnd.android.package-archive | -
**.jar** | application/java-archive | application/jar, application/jar-archive, application/x-java-archive
**.odt** | application/vnd.oasis.opendocument.text | application/x-vnd.oasis.opendocument.text
**.ott** | application/vnd.oasis.opendocument.text-template | application/x-vnd.oasis.opendocument.text-template
**.ods** | application/vnd.oasis.opendocument.spreadsheet | application/x-vnd.oasis.opendocument.spreadsheet
**.ots** | application/vnd.oasis.opendocument.spreadsheet-template | application/x-vnd.oasis.opendocument.spreadsheet-template
**.odp** | application/vnd.oasis.opendocument.presentation | application/x-vnd.oasis.opendocument.presentation
**.otp** | application/vnd.oasis.opendocument.presentation-template | application/x-vnd.oasis.opendocument.presentation-template
**.odg** | application/vnd.oasis.opendocument.graphics | application/x-vnd.oasis.opendocument.graphics
**.otg** | application/vnd.oasis.opendocument.graphics-template | application/x-vnd.oasis.opendocument.graphics-template
**.odf** | application/vnd.oasis.opendocument.formula | application/x-vnd.oasis.opendocument.formula
**.odc** | application/vnd.oasis.opendocument.chart | application/x-vnd.oasis.opendocument.chart
**.sxc** | application/vnd.sun.xml.calc | -
**.kmz** | application/vnd.google-earth.kmz | -
**.vsdx** | application/vnd.ms-visio.drawing.main+xml | -
**.pdf** | application/pdf | application/x-pdf
**.fdf** | application/vnd.fdf | -
**n/a** | application/x-ole-storage | -
**.msi** | application/x-ms-installer | application/x-windows-installer, application/x-msi
**.aaf** | application/octet-stream | -
**.msg** | application/vnd.ms-outlook | -
**.xls** | application/vnd.ms-excel | application/msexcel
**.pub** | application/vnd.ms-publisher | -
**.ppt** | application/vnd.ms-powerpoint | application/mspowerpoint
**.doc** | application/msword | application/vnd.ms-word
**.ps** | application/postscript | -
**.psd** | image/vnd.adobe.photoshop | image/x-psd, application/photoshop
**.p7s** | application/pkcs7-signature | -
**.ogg** | application/ogg | application/x-ogg
**.oga** | audio/ogg | -
**.ogv** | video/ogg | -
**.png** | image/png | -
**.png** | image/vnd.mozilla.apng | -
**.jpg** | image/jpeg | -
**.jxl** | image/jxl | -
**.jp2** | image/jp2 | -
**.jpf** | image/jpx | -
**.jpm** | image/jpm | video/jpm
**.jxs** | image/jxs | -
**.gif** | image/gif | -
**.webp** | image/webp | -
**.exe** | application/vnd.microsoft.portable-executable | -
**n/a** | application/x-elf | -
**n/a** | application/x-object | -
**n/a** | application/x-executable | -
**.so** | application/x-sharedlib | -
**n/a** | application/x-coredump | -
**.a** | application/x-archive | application/x-unix-archive
**.deb** | application/vnd.debian.binary-package | -
**.tar** | application/x-tar | -
**.xar** | application/x-xar | -
**.bz2** | application/x-bzip2 | -
**.fits** | application/fits | image/fits
**.tiff** | image/tiff | -
**.bmp** | image/bmp | image/x-bmp, image/x-ms-bmp
**.123** | application/vnd.lotus-1-2-3 | -
**.ico** | image/x-icon | -
**.mp3** | audio/mpeg | audio/x-mpeg, audio/mp3
**.flac** | audio/flac | -
**.midi** | audio/midi | audio/mid, audio/sp-midi, audio/x-mid, audio/x-midi
**.ape** | audio/ape | -
**.mpc** | audio/musepack | -
**.amr** | audio/amr | audio/amr-nb
**.wav** | audio/wav | audio/x-wav, audio/vnd.wave, audio/wave
**.aiff** | audio/aiff | audio/x-aiff
**.au** | audio/basic | -
**.mpeg** | video/mpeg | -
**.mov** | video/quicktime | -
**.mp4** | video/mp4 | -
**.avif** | image/avif | -
**.3gp** | video/3gpp | video/3gp, audio/3gpp
**.3g2** | video/3gpp2 | video/3g2, audio/3gpp2
**.mp4** | audio/mp4 | audio/x-mp4a
**.mqv** | video/quicktime | -
**.m4a** | audio/x-m4a | -
**.m4v** | video/x-m4v | -
**.heic** | image/heic | -
**.heic** | image/heic-sequence | -
**.heif** | image/heif | -
**.heif** | image/heif-sequence | -
**.mj2** | video/mj2 | -
**.dvb** | video/vnd.dvb.file | -
**.webm** | video/webm | audio/webm
**.avi** | video/x-msvideo | video/avi, video/msvideo
**.flv** | video/x-flv | -
**.mkv** | video/x-matroska | -
**.asf** | video/x-ms-asf | video/asf, video/x-ms-wmv
**.aac** | audio/aac | -
**.voc** | audio/x-unknown | -
**.m3u** | application/vnd.apple.mpegurl | audio/mpegurl
**.rmvb** | application/vnd.rn-realmedia-vbr | -
**.gz** | application/gzip | application/x-gzip, application/x-gunzip, application/gzipped, application/gzip-compressed, application/x-gzip-compressed, gzip/document
**.class** | application/x-java-applet | -
**.swf** | application/x-shockwave-flash | -
**.crx** | application/x-chrome-extension | -
**.ttf** | font/ttf | font/sfnt, application/x-font-ttf, application/font-sfnt
**.woff** | font/woff | -
**.woff2** | font/woff2 | -
**.otf** | font/otf | -
**.ttc** | font/collection | -
**.eot** | application/vnd.ms-fontobject | -
**.wasm** | application/wasm | -
**.shx** | application/vnd.shx | -
**.shp** | application/vnd.shp | -
**.dbf** | application/x-dbf | -
**.dcm** | application/dicom | -
**.rar** | application/x-rar-compressed | application/x-rar
**.djvu** | image/vnd.djvu | -
**.mobi** | application/x-mobipocket-ebook | -
**.lit** | application/x-ms-reader | -
**.bpg** | image/bpg | -
**.cbor** | application/cbor | -
**.sqlite** | application/vnd.sqlite3 | application/x-sqlite3
**.dwg** | image/vnd.dwg | image/x-dwg, application/acad, application/x-acad, application/autocad_dwg, application/dwg, application/x-dwg, application/x-autocad, drawing/dwg
**.nes** | application/vnd.nintendo.snes.rom | -
**.lnk** | application/x-ms-shortcut | -
**.macho** | application/x-mach-binary | -
**.qcp** | audio/qcelp | -
**.icns** | image/x-icns | -
**.hdr** | image/vnd.radiance | -
**.mrc** | application/marc | -
**.mdb** | application/x-msaccess | -
**.accdb** | application/x-msaccess | -
**.zst** | application/zstd | -
**.cab** | application/vnd.ms-cab-compressed | -
**.rpm** | application/x-rpm | -
**.xz** | application/x-xz | -
**.lz** | application/lzip | application/x-lzip
**.torrent** | application/x-bittorrent | -
**.cpio** | application/x-cpio | -
**n/a** | application/tzif | -
**.xcf** | image/x-xcf | -
**.pat** | image/x-gimp-pat | -
**.gbr** | image/x-gimp-gbr | -
**.glb** | model/gltf-binary | -
**.cab** | application/x-installshield | -
**.jxr** | image/jxr | image/vnd.ms-photo
**.parquet** | application/vnd.apache.parquet | application/x-parquet
**.one** | application/onenote | -
**.chm** | application/vnd.ms-htmlhelp | -
**.txt** | text/plain | -
**.svg** | image/svg+xml | -
**.html** | text/html | -
**.xml** | text/xml | application/xml
**.rss** | application/rss+xml | text/rss
**.atom** | application/atom+xml | -
**.x3d** | model/x3d+xml | -
**.kml** | application/vnd.google-earth.kml+xml | -
**.xlf** | application/x-xliff+xml | -
**.dae** | model/vnd.collada+xml | -
**.gml** | application/gml+xml | -
**.gpx** | application/gpx+xml | -
**.tcx** | application/vnd.garmin.tcx+xml | -
**.amf** | application/x-amf | -
**.3mf** | application/vnd.ms-package.3dmanufacturing-3dmodel+xml | -
**.xfdf** | application/vnd.adobe.xfdf | -
**.owl** | application/owl+xml | -
**.html** | application/xhtml+xml | -
**.php** | text/x-php | -
**.js** | text/javascript | application/x-javascript, application/javascript
**.lua** | text/x-lua | -
**.pl** | text/x-perl | -
**.py** | text/x-python | text/x-script.python, application/x-python
**.rb** | text/x-ruby | application/x-ruby
**.json** | application/json | -
**.geojson** | application/geo+json | -
**.har** | application/json | -
**.gltf** | model/gltf+json | -
**.ndjson** | application/x-ndjson | -
**.rtf** | text/rtf | application/rtf
**.srt** | application/x-subrip | application/x-srt, text/x-srt
**.tcl** | text/x-tcl | application/x-tcl
**.csv** | text/csv | -
**.tsv** | text/tab-separated-values | -
**.vcf** | text/vcard | -
**.ics** | text/calendar | -
**.warc** | application/warc | -
**.vtt** | text/vtt | -
**.sh** | text/x-shellscript | text/x-sh, application/x-shellscript, application/x-sh
**.pbm** | image/x-portable-bitmap | -
**.pgm** | image/x-portable-graymap | -
**.ppm** | image/x-portable-pixmap | -
**.pam** | image/x-portable-arbitrarymap | -
Extension | MIME type <br> Aliases | Hierarchy
--------- | ---------------------- | ---------
**n/a** | **application/octet-stream** | root
**.xpm** | **image/x-xpixmap** | xpm>root
**.7z** | **application/x-7z-compressed** | 7z>root
**.zip** | **application/zip** <br> application/x-zip, application/x-zip-compressed | zip>root
**.docx** | **application/vnd.openxmlformats-officedocument.wordprocessingml.document** | docx>zip>root
**.pptx** | **application/vnd.openxmlformats-officedocument.presentationml.presentation** | pptx>zip>root
**.xlsx** | **application/vnd.openxmlformats-officedocument.spreadsheetml.sheet** | xlsx>zip>root
**.epub** | **application/epub+zip** | epub>zip>root
**.apk** | **application/vnd.android.package-archive** | apk>zip>root
**.jar** | **application/java-archive** <br> application/jar, application/jar-archive, application/x-java-archive | jar>zip>root
**.odt** | **application/vnd.oasis.opendocument.text** <br> application/x-vnd.oasis.opendocument.text | odt>zip>root
**.ott** | **application/vnd.oasis.opendocument.text-template** <br> application/x-vnd.oasis.opendocument.text-template | ott>odt>zip>root
**.ods** | **application/vnd.oasis.opendocument.spreadsheet** <br> application/x-vnd.oasis.opendocument.spreadsheet | ods>zip>root
**.ots** | **application/vnd.oasis.opendocument.spreadsheet-template** <br> application/x-vnd.oasis.opendocument.spreadsheet-template | ots>ods>zip>root
**.odp** | **application/vnd.oasis.opendocument.presentation** <br> application/x-vnd.oasis.opendocument.presentation | odp>zip>root
**.otp** | **application/vnd.oasis.opendocument.presentation-template** <br> application/x-vnd.oasis.opendocument.presentation-template | otp>odp>zip>root
**.odg** | **application/vnd.oasis.opendocument.graphics** <br> application/x-vnd.oasis.opendocument.graphics | odg>zip>root
**.otg** | **application/vnd.oasis.opendocument.graphics-template** <br> application/x-vnd.oasis.opendocument.graphics-template | otg>odg>zip>root
**.odf** | **application/vnd.oasis.opendocument.formula** <br> application/x-vnd.oasis.opendocument.formula | odf>zip>root
**.odc** | **application/vnd.oasis.opendocument.chart** <br> application/x-vnd.oasis.opendocument.chart | odc>zip>root
**.sxc** | **application/vnd.sun.xml.calc** | sxc>zip>root
**.kmz** | **application/vnd.google-earth.kmz** | kmz>zip>root
**.vsdx** | **application/vnd.ms-visio.drawing.main+xml** | vsdx>zip>root
**.pdf** | **application/pdf** <br> application/x-pdf | pdf>root
**.fdf** | **application/vnd.fdf** | fdf>root
**n/a** | **application/x-ole-storage** | x-ole-storage>root
**.msi** | **application/x-ms-installer** <br> application/x-windows-installer, application/x-msi | msi>x-ole-storage>root
**.msg** | **application/vnd.ms-outlook** | msg>x-ole-storage>root
**.xls** | **application/vnd.ms-excel** <br> application/msexcel | xls>x-ole-storage>root
**.pub** | **application/vnd.ms-publisher** | pub>x-ole-storage>root
**.ppt** | **application/vnd.ms-powerpoint** <br> application/mspowerpoint | ppt>x-ole-storage>root
**.doc** | **application/msword** <br> application/vnd.ms-word | doc>x-ole-storage>root
**.ps** | **application/postscript** | ps>root
**.psd** | **image/vnd.adobe.photoshop** <br> image/x-psd, application/photoshop | psd>root
**.p7s** | **application/pkcs7-signature** | p7s>root
**.ogg** | **application/ogg** <br> application/x-ogg | ogg>root
**.oga** | **audio/ogg** | oga>ogg>root
**.ogv** | **video/ogg** | ogv>ogg>root
**.png** | **image/png** | png>root
**.png** | **image/vnd.mozilla.apng** | png>png>root
**.jpg** | **image/jpeg** | jpg>root
**.jxl** | **image/jxl** | jxl>root
**.jp2** | **image/jp2** | jp2>root
**.jpf** | **image/jpx** | jpf>root
**.jpm** | **image/jpm** <br> video/jpm | jpm>root
**.jxs** | **image/jxs** | jxs>root
**.gif** | **image/gif** | gif>root
**.webp** | **image/webp** | webp>root
**.exe** | **application/vnd.microsoft.portable-executable** | exe>root
**n/a** | **application/x-elf** | x-elf>root
**n/a** | **application/x-object** | x-object>x-elf>root
**n/a** | **application/x-executable** | x-executable>x-elf>root
**.so** | **application/x-sharedlib** | so>x-elf>root
**n/a** | **application/x-coredump** | x-coredump>x-elf>root
**.a** | **application/x-archive** <br> application/x-unix-archive | a>root
**.deb** | **application/vnd.debian.binary-package** | deb>a>root
**.tar** | **application/x-tar** | tar>root
**.xar** | **application/x-xar** | xar>root
**.bz2** | **application/x-bzip2** | bz2>root
**.fits** | **application/fits** <br> image/fits | fits>root
**.tiff** | **image/tiff** | tiff>root
**.bmp** | **image/bmp** <br> image/x-bmp, image/x-ms-bmp | bmp>root
**.123** | **application/vnd.lotus-1-2-3** | 123>root
**.ico** | **image/x-icon** | ico>root
**.mp3** | **audio/mpeg** <br> audio/x-mpeg, audio/mp3 | mp3>root
**.flac** | **audio/flac** | flac>root
**.midi** | **audio/midi** <br> audio/mid, audio/sp-midi, audio/x-mid, audio/x-midi | midi>root
**.ape** | **audio/ape** | ape>root
**.mpc** | **audio/musepack** | mpc>root
**.amr** | **audio/amr** <br> audio/amr-nb | amr>root
**.wav** | **audio/wav** <br> audio/x-wav, audio/vnd.wave, audio/wave | wav>root
**.aiff** | **audio/aiff** <br> audio/x-aiff | aiff>root
**.au** | **audio/basic** | au>root
**.mpeg** | **video/mpeg** | mpeg>root
**.mov** | **video/quicktime** | mov>root
**.mp4** | **video/mp4** | mp4>root
**.avif** | **image/avif** | avif>mp4>root
**.3gp** | **video/3gpp** <br> video/3gp, audio/3gpp | 3gp>mp4>root
**.3g2** | **video/3gpp2** <br> video/3g2, audio/3gpp2 | 3g2>mp4>root
**.mp4** | **audio/mp4** <br> audio/x-mp4a | mp4>mp4>root
**.mqv** | **video/quicktime** | mqv>mp4>root
**.m4a** | **audio/x-m4a** | m4a>mp4>root
**.m4v** | **video/x-m4v** | m4v>mp4>root
**.heic** | **image/heic** | heic>mp4>root
**.heic** | **image/heic-sequence** | heic>mp4>root
**.heif** | **image/heif** | heif>mp4>root
**.heif** | **image/heif-sequence** | heif>mp4>root
**.mj2** | **video/mj2** | mj2>mp4>root
**.dvb** | **video/vnd.dvb.file** | dvb>mp4>root
**.webm** | **video/webm** <br> audio/webm | webm>root
**.avi** | **video/x-msvideo** <br> video/avi, video/msvideo | avi>root
**.flv** | **video/x-flv** | flv>root
**.mkv** | **video/x-matroska** | mkv>root
**.asf** | **video/x-ms-asf** <br> video/asf, video/x-ms-wmv | asf>root
**.aac** | **audio/aac** | aac>root
**.voc** | **audio/x-unknown** | voc>root
**.m3u** | **application/vnd.apple.mpegurl** <br> audio/mpegurl | m3u>root
**.rmvb** | **application/vnd.rn-realmedia-vbr** | rmvb>root
**.gz** | **application/gzip** <br> application/x-gzip, application/x-gunzip, application/gzipped, application/gzip-compressed, application/x-gzip-compressed, gzip/document | gz>root
**.class** | **application/x-java-applet** | class>root
**.swf** | **application/x-shockwave-flash** | swf>root
**.crx** | **application/x-chrome-extension** | crx>root
**.ttf** | **font/ttf** <br> font/sfnt, application/x-font-ttf, application/font-sfnt | ttf>root
**.woff** | **font/woff** | woff>root
**.woff2** | **font/woff2** | woff2>root
**.otf** | **font/otf** | otf>root
**.ttc** | **font/collection** | ttc>root
**.eot** | **application/vnd.ms-fontobject** | eot>root
**.wasm** | **application/wasm** | wasm>root
**.shx** | **application/vnd.shx** | shx>root
**.shp** | **application/vnd.shp** | shp>shx>root
**.dbf** | **application/x-dbf** | dbf>root
**.dcm** | **application/dicom** | dcm>root
**.rar** | **application/x-rar-compressed** <br> application/x-rar | rar>root
**.djvu** | **image/vnd.djvu** | djvu>root
**.mobi** | **application/x-mobipocket-ebook** | mobi>root
**.lit** | **application/x-ms-reader** | lit>root
**.bpg** | **image/bpg** | bpg>root
**.cbor** | **application/cbor** | cbor>root
**.sqlite** | **application/vnd.sqlite3** <br> application/x-sqlite3 | sqlite>root
**.dwg** | **image/vnd.dwg** <br> image/x-dwg, application/acad, application/x-acad, application/autocad_dwg, application/dwg, application/x-dwg, application/x-autocad, drawing/dwg | dwg>root
**.nes** | **application/vnd.nintendo.snes.rom** | nes>root
**.lnk** | **application/x-ms-shortcut** | lnk>root
**.macho** | **application/x-mach-binary** | macho>root
**.qcp** | **audio/qcelp** | qcp>root
**.icns** | **image/x-icns** | icns>root
**.hdr** | **image/vnd.radiance** | hdr>root
**.mrc** | **application/marc** | mrc>root
**.mdb** | **application/x-msaccess** | mdb>root
**.accdb** | **application/x-msaccess** | accdb>root
**.zst** | **application/zstd** | zst>root
**.cab** | **application/vnd.ms-cab-compressed** | cab>root
**.rpm** | **application/x-rpm** | rpm>root
**.xz** | **application/x-xz** | xz>root
**.lz** | **application/lzip** <br> application/x-lzip | lz>root
**.torrent** | **application/x-bittorrent** | torrent>root
**.cpio** | **application/x-cpio** | cpio>root
**n/a** | **application/tzif** | tzif>root
**.xcf** | **image/x-xcf** | xcf>root
**.pat** | **image/x-gimp-pat** | pat>root
**.gbr** | **image/x-gimp-gbr** | gbr>root
**.glb** | **model/gltf-binary** | glb>root
**.cab** | **application/x-installshield** | cab>root
**.jxr** | **image/jxr** <br> image/vnd.ms-photo | jxr>root
**.parquet** | **application/vnd.apache.parquet** <br> application/x-parquet | parquet>root
**.one** | **application/onenote** | one>root
**.chm** | **application/vnd.ms-htmlhelp** | chm>root
**.wpd** | **application/vnd.wordperfect** | wpd>root
**.dxf** | **image/vnd.dxf** | dxf>root
**.grb** | **application/grib** | grb>root
**n/a** | **application/zlib** | zlib>root
**.txt** | **text/plain** | txt>root
**.svg** | **image/svg+xml** | svg>txt>root
**.html** | **text/html** | html>txt>root
**.xml** | **text/xml** <br> application/xml | xml>txt>root
**.rss** | **application/rss+xml** <br> text/rss | rss>xml>txt>root
**.atom** | **application/atom+xml** | atom>xml>txt>root
**.x3d** | **model/x3d+xml** | x3d>xml>txt>root
**.kml** | **application/vnd.google-earth.kml+xml** | kml>xml>txt>root
**.xlf** | **application/x-xliff+xml** | xlf>xml>txt>root
**.dae** | **model/vnd.collada+xml** | dae>xml>txt>root
**.gml** | **application/gml+xml** | gml>xml>txt>root
**.gpx** | **application/gpx+xml** | gpx>xml>txt>root
**.tcx** | **application/vnd.garmin.tcx+xml** | tcx>xml>txt>root
**.amf** | **application/x-amf** | amf>xml>txt>root
**.3mf** | **application/vnd.ms-package.3dmanufacturing-3dmodel+xml** | 3mf>xml>txt>root
**.xfdf** | **application/vnd.adobe.xfdf** | xfdf>xml>txt>root
**.owl** | **application/owl+xml** | owl>xml>txt>root
**.html** | **application/xhtml+xml** | html>xml>txt>root
**.php** | **text/x-php** | php>txt>root
**.js** | **text/javascript** <br> application/x-javascript, application/javascript | js>txt>root
**.lua** | **text/x-lua** | lua>txt>root
**.pl** | **text/x-perl** | pl>txt>root
**.py** | **text/x-python** <br> text/x-script.python, application/x-python | py>txt>root
**.rb** | **text/x-ruby** <br> application/x-ruby | rb>txt>root
**.json** | **application/json** | json>txt>root
**.geojson** | **application/geo+json** | geojson>json>txt>root
**.har** | **application/json** | har>json>txt>root
**.gltf** | **model/gltf+json** | gltf>json>txt>root
**.ndjson** | **application/x-ndjson** | ndjson>txt>root
**.rtf** | **text/rtf** <br> application/rtf | rtf>txt>root
**.srt** | **application/x-subrip** <br> application/x-srt, text/x-srt | srt>txt>root
**.tcl** | **text/x-tcl** <br> application/x-tcl | tcl>txt>root
**.csv** | **text/csv** | csv>txt>root
**.tsv** | **text/tab-separated-values** | tsv>txt>root
**.vcf** | **text/vcard** | vcf>txt>root
**.ics** | **text/calendar** | ics>txt>root
**.warc** | **application/warc** | warc>txt>root
**.vtt** | **text/vtt** | vtt>txt>root
**.sh** | **text/x-shellscript** <br> text/x-sh, application/x-shellscript, application/x-sh | sh>txt>root
**.pbm** | **image/x-portable-bitmap** | pbm>txt>root
**.pgm** | **image/x-portable-graymap** | pgm>txt>root
**.ppm** | **image/x-portable-pixmap** | ppm>txt>root
**.pam** | **image/x-portable-arbitrarymap** | pam>txt>root
**.eml** | **message/rfc822** | eml>txt>root

View File

@@ -24,7 +24,7 @@ var root = newMIME("application/octet-stream", "",
woff2, otf, ttc, eot, wasm, shx, dbf, dcm, rar, djvu, mobi, lit, bpg, cbor,
sqlite3, dwg, nes, lnk, macho, qcp, icns, hdr, mrc, mdb, accdb, zstd, cab,
rpm, xz, lzip, torrent, cpio, tzif, xcf, pat, gbr, glb, cabIS, jxr, parquet,
oneNote, chm,
oneNote, chm, wpd, dxf, grib, zlib,
// Keep text last because it is the slowest check.
text,
)
@@ -65,10 +65,9 @@ var (
jar = newMIME("application/java-archive", ".jar", magic.Jar).
alias("application/jar", "application/jar-archive", "application/x-java-archive")
apk = newMIME("application/vnd.android.package-archive", ".apk", magic.APK)
ole = newMIME("application/x-ole-storage", "", magic.Ole, msi, aaf, msg, xls, pub, ppt, doc)
ole = newMIME("application/x-ole-storage", "", magic.Ole, msi, msg, xls, pub, ppt, doc)
msi = newMIME("application/x-ms-installer", ".msi", magic.Msi).
alias("application/x-windows-installer", "application/x-msi")
aaf = newMIME("application/octet-stream", ".aaf", magic.Aaf)
doc = newMIME("application/msword", ".doc", magic.Doc).
alias("application/vnd.ms-word")
ppt = newMIME("application/vnd.ms-powerpoint", ".ppt", magic.Ppt).
@@ -83,7 +82,7 @@ var (
alias("application/x-ogg")
oggAudio = newMIME("audio/ogg", ".oga", magic.OggAudio)
oggVideo = newMIME("video/ogg", ".ogv", magic.OggVideo)
text = newMIME("text/plain", ".txt", magic.Text, svg, html, xml, php, js, lua, perl, python, ruby, json, ndJSON, rtf, srt, tcl, csv, tsv, vCard, iCalendar, warc, vtt, shell, netpbm, netpgm, netppm, netpam)
text = newMIME("text/plain", ".txt", magic.Text, svg, html, xml, php, js, lua, perl, python, ruby, json, ndJSON, rtf, srt, tcl, csv, tsv, vCard, iCalendar, warc, vtt, shell, netpbm, netpgm, netppm, netpam, rfc822)
xml = newMIME("text/xml", ".xml", magic.XML, rss, atom, x3d, kml, xliff, collada, gml, gpx, tcx, amf, threemf, xfdf, owl2, xhtml).
alias("application/xml")
xhtml = newMIME("application/xhtml+xml", ".html", magic.XHTML)
@@ -286,4 +285,9 @@ var (
cbor = newMIME("application/cbor", ".cbor", magic.CBOR)
oneNote = newMIME("application/onenote", ".one", magic.One)
chm = newMIME("application/vnd.ms-htmlhelp", ".chm", magic.CHM)
wpd = newMIME("application/vnd.wordperfect", ".wpd", magic.WPD)
dxf = newMIME("image/vnd.dxf", ".dxf", magic.DXF)
rfc822 = newMIME("message/rfc822", ".eml", magic.RFC822)
grib = newMIME("application/grib", ".grb", magic.GRIB)
zlib = newMIME("application/zlib", "", magic.Zlib)
)

View File

@@ -32,6 +32,7 @@ linters:
- maintidx
- misspell
- mnd
- modernize
- nakedret
- nestif
- nilnil

View File

@@ -123,6 +123,7 @@ validate := validator.New(validator.WithRequiredStructEnabled())
| udp6_addr | User Datagram Protocol Address UDPv6 |
| udp_addr | User Datagram Protocol Address UDP |
| unix_addr | Unix domain socket end point Address |
| uds_exists | Unix domain socket exists (checks filesystem sockets and Linux abstract sockets) |
| uri | URI String |
| url | URL String |
| http_url | HTTP(s) URL String |
@@ -137,6 +138,7 @@ validate := validator.New(validator.WithRequiredStructEnabled())
| alpha | Alpha Only |
| alphaspace | Alpha Space |
| alphanum | Alphanumeric |
| alphanumspace | Alphanumeric Space |
| alphanumunicode | Alphanumeric Unicode |
| alphaunicode | Alpha Unicode |
| ascii | ASCII |
@@ -164,7 +166,8 @@ validate := validator.New(validator.WithRequiredStructEnabled())
| base64 | Base64 String |
| base64url | Base64URL String |
| base64rawurl | Base64RawURL String |
| bic | Business Identifier Code (ISO 9362) |
| bic_iso_9362_2014 | Business Identifier Code (ISO 9362:2014) |
| bic | Business Identifier Code (ISO 9362:2022) |
| bcp47_language_tag | Language tag (BCP 47) |
| btc_addr | Bitcoin Address |
| btc_addr_bech32 | Bitcoin Bech32 Address (segwit) |

View File

@@ -1,6 +1,7 @@
package validator
import (
"bufio"
"bytes"
"cmp"
"context"
@@ -15,6 +16,7 @@ import (
"net/url"
"os"
"reflect"
"runtime"
"strconv"
"strings"
"sync"
@@ -120,6 +122,7 @@ var (
"alpha": isAlpha,
"alphaspace": isAlphaSpace,
"alphanum": isAlphanum,
"alphanumspace": isAlphaNumericSpace,
"alphaunicode": isAlphaUnicode,
"alphanumunicode": isAlphanumUnicode,
"boolean": isBoolean,
@@ -205,6 +208,7 @@ var (
"ip6_addr": isIP6AddrResolvable,
"ip_addr": isIPAddrResolvable,
"unix_addr": isUnixAddrResolvable,
"uds_exists": isUnixDomainSocketExists,
"mac": isMAC,
"hostname": isHostnameRFC952, // RFC 952
"hostname_rfc1123": isHostnameRFC1123, // RFC 1123
@@ -237,7 +241,8 @@ var (
"bcp47_language_tag": isBCP47LanguageTag,
"postcode_iso3166_alpha2": isPostcodeByIso3166Alpha2,
"postcode_iso3166_alpha2_field": isPostcodeByIso3166Alpha2Field,
"bic": isIsoBicFormat,
"bic_iso_9362_2014": isIsoBic2014Format,
"bic": isIsoBic2022Format,
"semver": isSemverFormat,
"dns_rfc1035_label": isDnsRFC1035LabelFormat,
"credit_card": isCreditCard,
@@ -533,12 +538,20 @@ func hasMultiByteCharacter(fl FieldLevel) bool {
// isPrintableASCII is the validation function for validating if the field's value is a valid printable ASCII character.
func isPrintableASCII(fl FieldLevel) bool {
return printableASCIIRegex().MatchString(fl.Field().String())
field := fl.Field()
if field.Kind() == reflect.String {
return printableASCIIRegex().MatchString(field.String())
}
return false
}
// isASCII is the validation function for validating if the field's value is a valid ASCII character.
func isASCII(fl FieldLevel) bool {
return aSCIIRegex().MatchString(fl.Field().String())
field := fl.Field()
if field.Kind() == reflect.String {
return aSCIIRegex().MatchString(field.String())
}
return false
}
// isUUID5 is the validation function for validating if the field's value is a valid v5 UUID.
@@ -1773,6 +1786,11 @@ func isAlphaSpace(fl FieldLevel) bool {
return alphaSpaceRegex().MatchString(fl.Field().String())
}
// isAlphaNumericSpace is the validation function for validating if the current field's value is a valid alphanumeric value with spaces.
func isAlphaNumericSpace(fl FieldLevel) bool {
return alphanNumericSpaceRegex().MatchString(fl.Field().String())
}
// isAlphaUnicode is the validation function for validating if the current field's value is a valid alpha unicode value.
func isAlphaUnicode(fl FieldLevel) bool {
return alphaUnicodeRegex().MatchString(fl.Field().String())
@@ -1974,11 +1992,12 @@ func excludedUnless(fl FieldLevel) bool {
panic(fmt.Sprintf("Bad param number for excluded_unless %s", fl.FieldName()))
}
for i := 0; i < len(params); i += 2 {
if !requireCheckFieldValue(fl, params[i], params[i+1], false) {
return !hasValue(fl)
if requireCheckFieldValue(fl, params[i], params[i+1], false) {
return true
}
}
return true
return !hasValue(fl)
}
// excludedWith is the validation function
@@ -2595,6 +2614,70 @@ func isUnixAddrResolvable(fl FieldLevel) bool {
return err == nil
}
// isUnixDomainSocketExists is the validation function for validating if the field's value is an existing Unix domain socket.
// It handles both filesystem-based sockets and Linux abstract sockets.
// It always returns false for Windows.
func isUnixDomainSocketExists(fl FieldLevel) bool {
if runtime.GOOS == "windows" {
return false
}
sockpath := fl.Field().String()
if sockpath == "" {
return false
}
// On Linux, check for abstract sockets (prefixed with @)
if runtime.GOOS == "linux" && strings.HasPrefix(sockpath, "@") {
return isAbstractSocketExists(sockpath)
}
// For filesystem-based sockets, check if the path exists and is a socket
stats, err := os.Stat(sockpath)
if err != nil {
return false
}
return stats.Mode().Type() == fs.ModeSocket
}
// isAbstractSocketExists checks if a Linux abstract socket exists by reading /proc/net/unix.
// Abstract sockets are identified by an @ prefix in human-readable form.
func isAbstractSocketExists(sockpath string) bool {
file, err := os.Open("/proc/net/unix")
if err != nil {
return false
}
defer func() {
_ = file.Close()
}()
scanner := bufio.NewScanner(file)
// Skip the header line
if !scanner.Scan() {
return false
}
// Abstract sockets in /proc/net/unix are represented with @ prefix
// The socket path is the last field in each line
for scanner.Scan() {
line := scanner.Text()
fields := strings.Fields(line)
// The path is the last field (8th field typically)
if len(fields) >= 8 {
path := fields[len(fields)-1]
if path == sockpath {
return true
}
}
}
return false
}
func isIP4Addr(fl FieldLevel) bool {
val := fl.Field().String()
@@ -2943,11 +3026,18 @@ func isBCP47LanguageTag(fl FieldLevel) bool {
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isIsoBicFormat is the validation function for validating if the current field's value is a valid Business Identifier Code (SWIFT code), defined in ISO 9362
func isIsoBicFormat(fl FieldLevel) bool {
// isIsoBic2014Format is the validation function for validating if the current field's value is a valid Business Identifier Code (SWIFT code), defined in ISO 9362 2014
func isIsoBic2014Format(fl FieldLevel) bool {
bicString := fl.Field().String()
return bicRegex().MatchString(bicString)
return bic2014Regex().MatchString(bicString)
}
// isIsoBic2022Format is the validation function for validating if the current field's value is a valid Business Identifier Code (SWIFT code), defined in ISO 9362 2022
func isIsoBic2022Format(fl FieldLevel) bool {
bicString := fl.Field().String()
return bic2022Regex().MatchString(bicString)
}
// isSemverFormat is the validation function for validating if the current field's value is a valid semver version, defined in Semantic Versioning 2.0.0

View File

@@ -289,6 +289,24 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s
if wrapper, ok := v.validations[current.tag]; ok {
current.fn = wrapper.fn
current.runValidationWhenNil = wrapper.runValidationOnNil
} else if aliasTag, isAlias := v.aliases[current.tag]; isAlias {
aliasFirst, aliasLast := v.parseFieldTagsRecursive(aliasTag, fieldName, current.tag, true)
current.tag = aliasFirst.tag
current.fn = aliasFirst.fn
current.runValidationWhenNil = aliasFirst.runValidationWhenNil
current.hasParam = aliasFirst.hasParam
current.param = aliasFirst.param
current.typeof = aliasFirst.typeof
current.hasAlias = true
if aliasFirst.next != nil {
nextInChain := current.next
current.next = aliasFirst.next
aliasLast.next = nextInChain
aliasLast.isBlockEnd = false
current = aliasLast
}
} else {
panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, current.tag, fieldName)))
}

View File

@@ -258,7 +258,7 @@ var iso3166_2 = map[string]struct{}{
"BD-56": {}, "BD-57": {}, "BD-58": {}, "BD-59": {}, "BD-60": {},
"BD-61": {}, "BD-62": {}, "BD-63": {}, "BD-64": {}, "BD-A": {},
"BD-B": {}, "BD-C": {}, "BD-D": {}, "BD-E": {}, "BD-F": {},
"BD-G": {}, "BE-BRU": {}, "BE-VAN": {}, "BE-VBR": {}, "BE-VLG": {},
"BD-G": {}, "BD-H": {}, "BE-BRU": {}, "BE-VAN": {}, "BE-VBR": {}, "BE-VLG": {},
"BE-VLI": {}, "BE-VOV": {}, "BE-VWV": {}, "BE-WAL": {}, "BE-WBR": {},
"BE-WHT": {}, "BE-WLG": {}, "BE-WLX": {}, "BE-WNA": {}, "BF-01": {},
"BF-02": {}, "BF-03": {}, "BF-04": {}, "BF-05": {}, "BF-06": {},
@@ -573,7 +573,7 @@ var iso3166_2 = map[string]struct{}{
"ID-KB": {}, "ID-KI": {}, "ID-KU": {}, "ID-KR": {}, "ID-KS": {},
"ID-KT": {}, "ID-LA": {}, "ID-MA": {}, "ID-ML": {}, "ID-MU": {},
"ID-NB": {}, "ID-NT": {}, "ID-NU": {}, "ID-PA": {}, "ID-PB": {},
"ID-PE": {}, "ID-PP": {}, "ID-PS": {}, "ID-PT": {}, "ID-RI": {},
"ID-PD": {}, "ID-PE": {}, "ID-PP": {}, "ID-PS": {}, "ID-PT": {}, "ID-RI": {},
"ID-SA": {}, "ID-SB": {}, "ID-SG": {}, "ID-SL": {}, "ID-SM": {},
"ID-SN": {}, "ID-SR": {}, "ID-SS": {}, "ID-ST": {}, "ID-SU": {},
"ID-YO": {}, "IE-C": {}, "IE-CE": {}, "IE-CN": {}, "IE-CO": {},
@@ -587,7 +587,7 @@ var iso3166_2 = map[string]struct{}{
"IN-AS": {}, "IN-BR": {}, "IN-CH": {}, "IN-CT": {}, "IN-DH": {},
"IN-DL": {}, "IN-DN": {}, "IN-GA": {}, "IN-GJ": {}, "IN-HP": {},
"IN-HR": {}, "IN-JH": {}, "IN-JK": {}, "IN-KA": {}, "IN-KL": {},
"IN-LD": {}, "IN-MH": {}, "IN-ML": {}, "IN-MN": {}, "IN-MP": {},
"IN-LA": {}, "IN-LD": {}, "IN-MH": {}, "IN-ML": {}, "IN-MN": {}, "IN-MP": {},
"IN-MZ": {}, "IN-NL": {}, "IN-TG": {}, "IN-OR": {}, "IN-PB": {}, "IN-PY": {},
"IN-RJ": {}, "IN-SK": {}, "IN-TN": {}, "IN-TR": {}, "IN-UP": {},
"IN-UT": {}, "IN-WB": {}, "IQ-AN": {}, "IQ-AR": {}, "IQ-BA": {},
@@ -667,7 +667,7 @@ var iso3166_2 = map[string]struct{}{
"KP-08": {}, "KP-09": {}, "KP-10": {}, "KP-13": {}, "KR-11": {},
"KR-26": {}, "KR-27": {}, "KR-28": {}, "KR-29": {}, "KR-30": {},
"KR-31": {}, "KR-41": {}, "KR-42": {}, "KR-43": {}, "KR-44": {},
"KR-45": {}, "KR-46": {}, "KR-47": {}, "KR-48": {}, "KR-49": {},
"KR-45": {}, "KR-46": {}, "KR-47": {}, "KR-48": {}, "KR-49": {}, "KR-50": {},
"KW-AH": {}, "KW-FA": {}, "KW-HA": {}, "KW-JA": {}, "KW-KU": {},
"KW-MU": {}, "KZ-10": {}, "KZ-75": {}, "KZ-19": {}, "KZ-11": {},
"KZ-15": {}, "KZ-71": {}, "KZ-23": {}, "KZ-27": {}, "KZ-47": {},
@@ -758,7 +758,7 @@ var iso3166_2 = map[string]struct{}{
"ME-02": {}, "ME-03": {}, "ME-04": {}, "ME-05": {}, "ME-06": {},
"ME-07": {}, "ME-08": {}, "ME-09": {}, "ME-10": {}, "ME-11": {},
"ME-12": {}, "ME-13": {}, "ME-14": {}, "ME-15": {}, "ME-16": {},
"ME-17": {}, "ME-18": {}, "ME-19": {}, "ME-20": {}, "ME-21": {}, "ME-24": {},
"ME-17": {}, "ME-18": {}, "ME-19": {}, "ME-20": {}, "ME-21": {}, "ME-22": {}, "ME-23": {}, "ME-24": {}, "ME-25": {},
"MG-A": {}, "MG-D": {}, "MG-F": {}, "MG-M": {}, "MG-T": {},
"MG-U": {}, "MH-ALK": {}, "MH-ALL": {}, "MH-ARN": {}, "MH-AUR": {},
"MH-EBO": {}, "MH-ENI": {}, "MH-JAB": {}, "MH-JAL": {}, "MH-KIL": {},
@@ -856,7 +856,7 @@ var iso3166_2 = map[string]struct{}{
"NO-22": {}, "NP-1": {}, "NP-2": {}, "NP-3": {}, "NP-4": {},
"NP-5": {}, "NP-BA": {}, "NP-BH": {}, "NP-DH": {}, "NP-GA": {},
"NP-JA": {}, "NP-KA": {}, "NP-KO": {}, "NP-LU": {}, "NP-MA": {},
"NP-ME": {}, "NP-NA": {}, "NP-RA": {}, "NP-SA": {}, "NP-SE": {},
"NP-ME": {}, "NP-NA": {}, "NP-P1": {}, "NP-P2": {}, "NP-P3": {}, "NP-P4": {}, "NP-P5": {}, "NP-P6": {}, "NP-P7": {}, "NP-RA": {}, "NP-SA": {}, "NP-SE": {},
"NR-01": {}, "NR-02": {}, "NR-03": {}, "NR-04": {}, "NR-05": {},
"NR-06": {}, "NR-07": {}, "NR-08": {}, "NR-09": {}, "NR-10": {},
"NR-11": {}, "NR-12": {}, "NR-13": {}, "NR-14": {}, "NZ-AUK": {},

View File

@@ -10,33 +10,33 @@ var iso4217 = map[string]struct{}{
"BIF": {}, "CVE": {}, "KHR": {}, "XAF": {}, "CAD": {},
"KYD": {}, "CLP": {}, "CLF": {}, "CNY": {}, "COP": {},
"COU": {}, "KMF": {}, "CDF": {}, "NZD": {}, "CRC": {},
"HRK": {}, "CUP": {}, "CUC": {}, "ANG": {}, "CZK": {},
"DKK": {}, "DJF": {}, "DOP": {}, "EGP": {}, "SVC": {},
"ERN": {}, "SZL": {}, "ETB": {}, "FKP": {}, "FJD": {},
"XPF": {}, "GMD": {}, "GEL": {}, "GHS": {}, "GIP": {},
"GTQ": {}, "GBP": {}, "GNF": {}, "GYD": {}, "HTG": {},
"HNL": {}, "HKD": {}, "HUF": {}, "ISK": {}, "IDR": {},
"XDR": {}, "IRR": {}, "IQD": {}, "ILS": {}, "JMD": {},
"JPY": {}, "JOD": {}, "KZT": {}, "KES": {}, "KPW": {},
"KRW": {}, "KWD": {}, "KGS": {}, "LAK": {}, "LBP": {},
"LSL": {}, "ZAR": {}, "LRD": {}, "LYD": {}, "CHF": {},
"MOP": {}, "MKD": {}, "MGA": {}, "MWK": {}, "MYR": {},
"MVR": {}, "MRU": {}, "MUR": {}, "XUA": {}, "MXN": {},
"MXV": {}, "MDL": {}, "MNT": {}, "MAD": {}, "MZN": {},
"MMK": {}, "NAD": {}, "NPR": {}, "NIO": {}, "NGN": {},
"OMR": {}, "PKR": {}, "PAB": {}, "PGK": {}, "PYG": {},
"PEN": {}, "PHP": {}, "PLN": {}, "QAR": {}, "RON": {},
"RUB": {}, "RWF": {}, "SHP": {}, "WST": {}, "STN": {},
"SAR": {}, "RSD": {}, "SCR": {}, "SLL": {}, "SGD": {},
"XSU": {}, "SBD": {}, "SOS": {}, "SSP": {}, "LKR": {},
"SDG": {}, "SRD": {}, "SEK": {}, "CHE": {}, "CHW": {},
"SYP": {}, "TWD": {}, "TJS": {}, "TZS": {}, "THB": {},
"TOP": {}, "TTD": {}, "TND": {}, "TRY": {}, "TMT": {},
"UGX": {}, "UAH": {}, "AED": {}, "USN": {}, "UYU": {},
"UYI": {}, "UYW": {}, "UZS": {}, "VUV": {}, "VES": {},
"VND": {}, "YER": {}, "ZMW": {}, "ZWL": {}, "XBA": {},
"XBB": {}, "XBC": {}, "XBD": {}, "XTS": {}, "XXX": {},
"XAU": {}, "XPD": {}, "XPT": {}, "XAG": {},
"CUP": {}, "CZK": {}, "DKK": {}, "DJF": {}, "DOP": {},
"EGP": {}, "SVC": {}, "ERN": {}, "SZL": {}, "ETB": {},
"FKP": {}, "FJD": {}, "XPF": {}, "GMD": {}, "GEL": {},
"GHS": {}, "GIP": {}, "GTQ": {}, "GBP": {}, "GNF": {},
"GYD": {}, "HTG": {}, "HNL": {}, "HKD": {}, "HUF": {},
"ISK": {}, "IDR": {}, "XDR": {}, "IRR": {}, "IQD": {},
"ILS": {}, "JMD": {}, "JPY": {}, "JOD": {}, "KZT": {},
"KES": {}, "KPW": {}, "KRW": {}, "KWD": {}, "KGS": {},
"LAK": {}, "LBP": {}, "LSL": {}, "ZAR": {}, "LRD": {},
"LYD": {}, "CHF": {}, "MOP": {}, "MKD": {}, "MGA": {},
"MWK": {}, "MYR": {}, "MVR": {}, "MRU": {}, "MUR": {},
"XUA": {}, "MXN": {}, "MXV": {}, "MDL": {}, "MNT": {},
"MAD": {}, "MZN": {}, "MMK": {}, "NAD": {}, "NPR": {},
"NIO": {}, "NGN": {}, "OMR": {}, "PKR": {}, "PAB": {},
"PGK": {}, "PYG": {}, "PEN": {}, "PHP": {}, "PLN": {},
"QAR": {}, "RON": {}, "RUB": {}, "RWF": {}, "SHP": {},
"WST": {}, "STN": {}, "SAR": {}, "RSD": {}, "SCR": {},
"SLE": {}, "SGD": {}, "XSU": {}, "SBD": {}, "SOS": {},
"SSP": {}, "LKR": {}, "SDG": {}, "SRD": {}, "SEK": {},
"CHE": {}, "CHW": {}, "SYP": {}, "TWD": {}, "TJS": {},
"TZS": {}, "THB": {}, "TOP": {}, "TTD": {}, "TND": {},
"TRY": {}, "TMT": {}, "UGX": {}, "UAH": {}, "AED": {},
"USN": {}, "UYU": {}, "UYI": {}, "UYW": {}, "UZS": {},
"VUV": {}, "VES": {}, "VED": {}, "VND": {}, "YER": {},
"ZMW": {}, "ZWG": {}, "XBA": {}, "XBB": {}, "XBC": {},
"XBD": {}, "XCG": {}, "XTS": {}, "XXX": {}, "XAU": {},
"XPD": {}, "XPT": {}, "XAG": {},
}
var iso4217_numeric = map[int]struct{}{
@@ -45,35 +45,35 @@ var iso4217_numeric = map[int]struct{}{
64: {}, 68: {}, 72: {}, 84: {}, 90: {},
96: {}, 104: {}, 108: {}, 116: {}, 124: {},
132: {}, 136: {}, 144: {}, 152: {}, 156: {},
170: {}, 174: {}, 188: {}, 191: {}, 192: {},
203: {}, 208: {}, 214: {}, 222: {}, 230: {},
232: {}, 238: {}, 242: {}, 262: {}, 270: {},
292: {}, 320: {}, 324: {}, 328: {}, 332: {},
340: {}, 344: {}, 348: {}, 352: {}, 356: {},
360: {}, 364: {}, 368: {}, 376: {}, 388: {},
392: {}, 398: {}, 400: {}, 404: {}, 408: {},
410: {}, 414: {}, 417: {}, 418: {}, 422: {},
426: {}, 430: {}, 434: {}, 446: {}, 454: {},
458: {}, 462: {}, 480: {}, 484: {}, 496: {},
498: {}, 504: {}, 512: {}, 516: {}, 524: {},
532: {}, 533: {}, 548: {}, 554: {}, 558: {},
566: {}, 578: {}, 586: {}, 590: {}, 598: {},
600: {}, 604: {}, 608: {}, 634: {}, 643: {},
646: {}, 654: {}, 682: {}, 690: {}, 694: {},
702: {}, 704: {}, 706: {}, 710: {}, 728: {},
748: {}, 752: {}, 756: {}, 760: {}, 764: {},
776: {}, 780: {}, 784: {}, 788: {}, 800: {},
807: {}, 818: {}, 826: {}, 834: {}, 840: {},
858: {}, 860: {}, 882: {}, 886: {}, 901: {},
927: {}, 928: {}, 929: {}, 930: {}, 931: {},
932: {}, 933: {}, 934: {}, 936: {}, 938: {},
940: {}, 941: {}, 943: {}, 944: {}, 946: {},
947: {}, 948: {}, 949: {}, 950: {}, 951: {},
952: {}, 953: {}, 955: {}, 956: {}, 957: {},
958: {}, 959: {}, 960: {}, 961: {}, 962: {},
963: {}, 964: {}, 965: {}, 967: {}, 968: {},
969: {}, 970: {}, 971: {}, 972: {}, 973: {},
975: {}, 976: {}, 977: {}, 978: {}, 979: {},
980: {}, 981: {}, 984: {}, 985: {}, 986: {},
990: {}, 994: {}, 997: {}, 999: {},
170: {}, 174: {}, 188: {}, 192: {}, 203: {},
208: {}, 214: {}, 222: {}, 230: {}, 232: {},
238: {}, 242: {}, 262: {}, 270: {}, 292: {},
320: {}, 324: {}, 328: {}, 332: {}, 340: {},
344: {}, 348: {}, 352: {}, 356: {}, 360: {},
364: {}, 368: {}, 376: {}, 388: {}, 392: {},
398: {}, 400: {}, 404: {}, 408: {}, 410: {},
414: {}, 417: {}, 418: {}, 422: {}, 426: {},
430: {}, 434: {}, 446: {}, 454: {}, 458: {},
462: {}, 480: {}, 484: {}, 496: {}, 498: {},
504: {}, 512: {}, 516: {}, 524: {}, 532: {},
533: {}, 548: {}, 554: {}, 558: {}, 566: {},
578: {}, 586: {}, 590: {}, 598: {}, 600: {},
604: {}, 608: {}, 634: {}, 643: {}, 646: {},
654: {}, 682: {}, 690: {}, 702: {}, 704: {},
706: {}, 710: {}, 728: {}, 748: {}, 752: {},
756: {}, 760: {}, 764: {}, 776: {}, 780: {},
784: {}, 788: {}, 800: {}, 807: {}, 818: {},
826: {}, 834: {}, 840: {}, 858: {}, 860: {},
882: {}, 886: {}, 901: {}, 924: {}, 925: {},
926: {}, 927: {}, 928: {}, 929: {}, 930: {},
933: {}, 934: {}, 936: {}, 938: {}, 940: {},
941: {}, 943: {}, 944: {}, 946: {}, 947: {},
948: {}, 949: {}, 950: {}, 951: {}, 952: {},
953: {}, 955: {}, 956: {}, 957: {}, 958: {},
959: {}, 960: {}, 961: {}, 962: {}, 963: {},
964: {}, 965: {}, 967: {}, 968: {}, 969: {},
970: {}, 971: {}, 972: {}, 973: {}, 975: {},
976: {}, 977: {}, 978: {}, 979: {}, 980: {},
981: {}, 984: {}, 985: {}, 986: {}, 990: {},
994: {}, 997: {}, 999: {},
}

View File

@@ -201,6 +201,15 @@ only for the nil-values).
Usage: omitnil
# Omit Zero
Allows to skip the validation if the value is a zero value.
For pointers, it checks if the pointer is nil or the underlying value is a zero value.
For slices and maps, it checks if the value is nil or empty.
Otherwise, behaves the same as omitempty.
Usage: omitzero
# Dive
This tells the validator to dive into a slice, array or map and validate that
@@ -789,6 +798,12 @@ This validates that a string value contains ASCII alphanumeric characters only
Usage: alphanum
# Alphanumeric Space
This validates that a string value contains ASCII alphanumeric characters and spaces only
Usage: alphanumspace
# Alpha Unicode
This validates that a string value contains unicode alpha characters only
@@ -1263,6 +1278,15 @@ This validates that a string value contains a valid Unix Address.
Usage: unix_addr
# Unix Domain Socket Exists
This validates that a Unix domain socket file exists at the specified path.
It checks both filesystem-based sockets and Linux abstract sockets (prefixed with @).
For filesystem sockets, it verifies the path exists and is a socket file.
For abstract sockets on Linux, it checks /proc/net/unix.
Usage: uds_exists
# Media Access Control Address MAC
This validates that a string value contains a valid MAC Address.
@@ -1378,13 +1402,20 @@ More information on https://pkg.go.dev/golang.org/x/text/language
Usage: bcp47_language_tag
BIC (SWIFT code)
BIC (SWIFT code - 2022 standard)
This validates that a string value is a valid Business Identifier Code (SWIFT code), defined in ISO 9362.
More information on https://www.iso.org/standard/60390.html
This validates that a string value is a valid Business Identifier Code (SWIFT code), defined in ISO 9362:2022.
More information on https://www.iso.org/standard/84108.html
Usage: bic
BIC (SWIFT code - 2014 standard)
This validates that a string value is a valid Business Identifier Code (SWIFT code), defined in ISO 9362:2014.
More information on https://www.iso.org/standard/60390.html
Usage: bic_iso_9362_2014
# RFC 1035 label
This validates that a string value is a valid dns RFC 1035 label, defined in RFC 1035.
@@ -1519,7 +1550,7 @@ This package panics when bad input is provided, this is by design, bad code like
that should not make it to production.
type Test struct {
TestField string `validate:"nonexistantfunction=1"`
TestField string `validate:"nonexistentfunction=1"`
}
t := &Test{

View File

@@ -9,6 +9,7 @@ const (
alphaRegexString = "^[a-zA-Z]+$"
alphaSpaceRegexString = "^[a-zA-Z ]+$"
alphaNumericRegexString = "^[a-zA-Z0-9]+$"
alphaNumericSpaceRegexString = "^[a-zA-Z0-9 ]+$"
alphaUnicodeRegexString = "^[\\p{L}]+$"
alphaUnicodeNumericRegexString = "^[\\p{L}\\p{N}]+$"
numericRegexString = "^[-+]?[0-9]+(?:\\.[0-9]+)?$"
@@ -20,7 +21,7 @@ const (
hslRegexString = "^hsl\\(\\s*(?:0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*\\)$"
hslaRegexString = "^hsla\\(\\s*(?:0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0.[1-9]*)|[01])\\s*\\)$"
emailRegexString = "^(?:(?:(?:(?:[a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(?:\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|(?:(?:\\x22)(?:(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(?:\\x20|\\x09)+)?(?:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(\\x20|\\x09)+)?(?:\\x22))))@(?:(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$"
e164RegexString = "^\\+[1-9]?[0-9]{7,14}$"
e164RegexString = "^\\+?[1-9]\\d{7,14}$"
base32RegexString = "^(?:[A-Z2-7]{8})*(?:[A-Z2-7]{2}={6}|[A-Z2-7]{4}={4}|[A-Z2-7]{5}={3}|[A-Z2-7]{7}=|[A-Z2-7]{8})$"
base64RegexString = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$"
base64URLRegexString = "^(?:[A-Za-z0-9-_]{4})*(?:[A-Za-z0-9-_]{2}==|[A-Za-z0-9-_]{3}=|[A-Za-z0-9-_]{4})$"
@@ -68,7 +69,8 @@ const (
hTMLRegexString = `<[/]?([a-zA-Z]+).*?>`
jWTRegexString = "^[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]*$"
splitParamsRegexString = `'[^']*'|\S+`
bicRegexString = `^[A-Za-z]{6}[A-Za-z0-9]{2}([A-Za-z0-9]{3})?$`
bic2014RegexString = `^[A-Za-z]{6}[A-Za-z0-9]{2}([A-Za-z0-9]{3})?$`
bic2022RegexString = `^[A-Z0-9]{4}[A-Z]{2}[A-Z0-9]{2}(?:[A-Z0-9]{3})?$`
semverRegexString = `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$` // numbered capture groups https://semver.org/
dnsRegexStringRFC1035Label = "^[a-z]([-a-z0-9]*[a-z0-9])?$"
cveRegexString = `^CVE-(1999|2\d{3})-(0[^0]\d{2}|0\d[^0]\d{1}|0\d{2}[^0]|[1-9]{1}\d{3,})$` // CVE Format Id https://cve.mitre.org/cve/identifiers/syntaxchange.html
@@ -95,6 +97,7 @@ func lazyRegexCompile(str string) func() *regexp.Regexp {
var (
alphaRegex = lazyRegexCompile(alphaRegexString)
alphaSpaceRegex = lazyRegexCompile(alphaSpaceRegexString)
alphanNumericSpaceRegex = lazyRegexCompile(alphaNumericSpaceRegexString)
alphaNumericRegex = lazyRegexCompile(alphaNumericRegexString)
alphaUnicodeRegex = lazyRegexCompile(alphaUnicodeRegexString)
alphaUnicodeNumericRegex = lazyRegexCompile(alphaUnicodeNumericRegexString)
@@ -153,7 +156,8 @@ var (
hTMLRegex = lazyRegexCompile(hTMLRegexString)
jWTRegex = lazyRegexCompile(jWTRegexString)
splitParamsRegex = lazyRegexCompile(splitParamsRegexString)
bicRegex = lazyRegexCompile(bicRegexString)
bic2014Regex = lazyRegexCompile(bic2014RegexString)
bic2022Regex = lazyRegexCompile(bic2022RegexString)
semverRegex = lazyRegexCompile(semverRegexString)
dnsRegexRFC1035Label = lazyRegexCompile(dnsRegexStringRFC1035Label)
cveRegex = lazyRegexCompile(cveRegexString)

View File

@@ -1074,6 +1074,26 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} can only contain alphanumeric characters",
override: false,
},
{
tag: "alphaspace",
translation: "{0} can only contain alphabetic and space characters",
override: false,
},
{
tag: "alphanumspace",
translation: "{0} can only contain alphanumeric and space characters",
override: false,
},
{
tag: "alphaunicode",
translation: "{0} can only contain unicode alphabetic characters",
override: false,
},
{
tag: "alphanumunicode",
translation: "{0} can only contain unicode alphanumeric characters",
override: false,
},
{
tag: "numeric",
translation: "{0} must be a valid numeric value",

View File

@@ -214,7 +214,9 @@ BEGIN:
}
// if got here there was more namespace, cannot go any deeper
panic("Invalid field namespace")
// return found=false instead of panicking to handle cases like ValidateMap
// where cross-field validators (required_if, etc.) can't navigate non-struct parents
return
}
// asInt returns the parameter as an int64

View File

@@ -1,5 +1,12 @@
run:
timeout: 10m
version: "2"
formatters:
enable:
- gofumpt
- goimports
settings:
gofumpt:
extra-rules: true
linters:
enable:
@@ -18,9 +25,7 @@ linters:
- gocritic
- godot
- godox
- gofumpt
- goheader
- goimports
- gomoddirectives
- goprintffuncname
- gosec
@@ -31,84 +36,81 @@ linters:
- misspell
- nolintlint
- revive
- stylecheck
- tenv
- staticcheck
- testifylint
- thelper
- unconvert
- unparam
- usestdlibvars
- whitespace
linters-settings:
misspell:
locale: US
godox:
keywords:
- FIXME
goheader:
template: |-
Copyright 2015 Tim Heckman. All rights reserved.
Copyright 2018-{{ YEAR }} The Gofrs. All rights reserved.
Use of this source code is governed by the BSD 3-Clause
license that can be found in the LICENSE file.
gofumpt:
extra-rules: true
gocritic:
enabled-tags:
- diagnostic
- style
- performance
disabled-checks:
- paramTypeCombine # already handle by gofumpt.extra-rules
- whyNoLint # already handle by nonolint
- unnamedResult
- hugeParam
- sloppyReassign
- rangeValCopy
- octalLiteral
- ptrToRefParam
- appendAssign
- ruleguard
- httpNoBody
- exposedSyncMutex
revive:
rules:
- name: struct-tag
- name: blank-imports
- name: context-as-argument
- name: context-keys-type
- name: dot-imports
- name: error-return
- name: error-strings
- name: error-naming
- name: exported
- name: if-return
- name: increment-decrement
- name: var-naming
- name: var-declaration
- name: package-comments
- name: range
- name: receiver-naming
- name: time-naming
- name: unexported-return
- name: indent-error-flow
- name: errorf
- name: empty-block
- name: superfluous-else
- name: unused-parameter
- name: unreachable-code
- name: redefines-builtin-id
- wsl_v5
settings:
gocritic:
disabled-checks:
- paramTypeCombine # already handle by gofumpt.extra-rules
- whyNoLint # already handle by nonolint
- unnamedResult
- hugeParam
- sloppyReassign
- rangeValCopy
- octalLiteral
- ptrToRefParam
- appendAssign
- ruleguard
- httpNoBody
- exposedSyncMutex
enabled-tags:
- diagnostic
- style
- performance
godox:
keywords:
- FIXME
goheader:
template: |-
Copyright 2015 Tim Heckman. All rights reserved.
Copyright 2018-{{ YEAR }} The Gofrs. All rights reserved.
Use of this source code is governed by the BSD 3-Clause
license that can be found in the LICENSE file.
gosec:
excludes:
- G115
misspell:
locale: US
revive:
rules:
- name: struct-tag
- name: blank-imports
- name: context-as-argument
- name: context-keys-type
- name: dot-imports
- name: error-return
- name: error-strings
- name: error-naming
- name: exported
- name: if-return
- name: increment-decrement
- name: var-naming
- name: var-declaration
- name: package-comments
- name: range
- name: receiver-naming
- name: time-naming
- name: unexported-return
- name: indent-error-flow
- name: errorf
- name: empty-block
- name: superfluous-else
- name: unused-parameter
- name: unreachable-code
- name: redefines-builtin-id
exclusions:
presets:
- comments
- common-false-positives
- std-error-handling
issues:
exclude-use-default: true
max-issues-per-linter: 0
max-same-issues: 0
output:
show-stats: true
sort-results: true
sort-order:
- linter
- file

View File

@@ -1,4 +1,4 @@
Copyright (c) 2018-2024, The Gofrs
Copyright (c) 2018-2025, The Gofrs
Copyright (c) 2015-2020, Tim Heckman
All rights reserved.

View File

@@ -1,5 +1,5 @@
// Copyright 2015 Tim Heckman. All rights reserved.
// Copyright 2018-2024 The Gofrs. All rights reserved.
// Copyright 2018-2025 The Gofrs. All rights reserved.
// Use of this source code is governed by the BSD 3-Clause
// license that can be found in the LICENSE file.
@@ -62,6 +62,7 @@ type Flock struct {
func New(path string, opts ...Option) *Flock {
// create it if it doesn't exist, and open the file read-only.
flags := os.O_CREATE
switch runtime.GOOS {
case "aix", "solaris", "illumos":
// AIX cannot preform write-lock (i.e. exclusive) on a read-only file.
@@ -124,6 +125,22 @@ func (f *Flock) RLocked() bool {
return f.r
}
// Stat returns the FileInfo structure describing the lock file.
// If the lock file does not exist or cannot be accessed, an error is returned.
//
// This can be used to check the modification time of the lock file,
// which is useful for detecting stale locks.
func (f *Flock) Stat() (fs.FileInfo, error) {
f.m.RLock()
defer f.m.RUnlock()
if f.fh != nil {
return f.fh.Stat()
}
return os.Stat(f.path)
}
func (f *Flock) String() string {
return f.path
}
@@ -158,7 +175,6 @@ func tryCtx(ctx context.Context, fn func() (bool, error), retryDelay time.Durati
case <-ctx.Done():
return false, ctx.Err()
case <-time.After(retryDelay):
// try again
}
}
}

View File

@@ -1,3 +1,8 @@
// Copyright 2015 Tim Heckman. All rights reserved.
// Copyright 2018-2025 The Gofrs. All rights reserved.
// Use of this source code is governed by the BSD 3-Clause
// license that can be found in the LICENSE file.
//go:build (!unix && !windows) || plan9
package flock

View File

@@ -1,5 +1,5 @@
// Copyright 2015 Tim Heckman. All rights reserved.
// Copyright 2018-2024 The Gofrs. All rights reserved.
// Copyright 2018-2025 The Gofrs. All rights reserved.
// Use of this source code is governed by the BSD 3-Clause
// license that can be found in the LICENSE file.
@@ -155,6 +155,7 @@ func (f *Flock) try(locked *bool, flag int) (bool, error) {
}
var retried bool
retry:
err := unix.Flock(int(f.fh.Fd()), flag|unix.LOCK_NB)

View File

@@ -1,5 +1,5 @@
// Copyright 2015 Tim Heckman. All rights reserved.
// Copyright 2018-2024 The Gofrs. All rights reserved.
// Copyright 2018-2025 The Gofrs. All rights reserved.
// Use of this source code is governed by the BSD 3-Clause
// license that can be found in the LICENSE file.

View File

@@ -1,5 +1,5 @@
// Copyright 2015 Tim Heckman. All rights reserved.
// Copyright 2018-2024 The Gofrs. All rights reserved.
// Copyright 2018-2025 The Gofrs. All rights reserved.
// Use of this source code is governed by the BSD 3-Clause
// license that can be found in the LICENSE file.
@@ -23,6 +23,8 @@ const winLockfileSharedLock = 0x00000000
// ErrorLockViolation is the error code returned from the Windows syscall when a lock would block,
// and you ask to fail immediately.
//
//nolint:errname // It should be renamed to `ErrLockViolation`.
const ErrorLockViolation windows.Errno = 0x21 // 33
// Lock is a blocking call to try and take an exclusive file lock.

View File

@@ -332,7 +332,10 @@ func DialURLContext(ctx context.Context, rawurl string, options ...DialOption) (
return nil, err
}
if u.Scheme != "redis" && u.Scheme != "rediss" {
switch u.Scheme {
case "redis", "rediss", "valkey", "valkeys":
// valid scheme
default:
return nil, fmt.Errorf("invalid redis URL scheme: %s", u.Scheme)
}
@@ -386,7 +389,7 @@ func DialURLContext(ctx context.Context, rawurl string, options ...DialOption) (
return nil, fmt.Errorf("invalid database: %s", u.Path[1:])
}
options = append(options, DialUseTLS(u.Scheme == "rediss"))
options = append(options, DialUseTLS(u.Scheme == "rediss" || u.Scheme == "valkeys"))
return DialContext(ctx, "tcp", address, options...)
}

View File

@@ -477,7 +477,7 @@ var errScanStructValue = errors.New("redigo.ScanStruct: value must be non-nil po
// ScanStruct uses exported field names to match values in the response. Use
// 'redis' field tag to override the name:
//
// Field int `redis:"myName"`
// Field int `redis:"myName"`
//
// Fields with the tag redis:"-" are ignored.
//
@@ -513,9 +513,9 @@ func ScanStruct(src []interface{}, dest interface{}) error {
continue
}
name, ok := src[i].([]byte)
name, ok := convertToBulk(src[i])
if !ok {
return fmt.Errorf("redigo.ScanStruct: key %d not a bulk string value", i)
return fmt.Errorf("redigo.ScanStruct: key %d not a bulk string value got type: %T", i, src[i])
}
fs := ss.fieldSpec(name)
@@ -530,6 +530,19 @@ func ScanStruct(src []interface{}, dest interface{}) error {
return nil
}
// convertToBulk converts src to a []byte if src is a string or bulk string
// and returns true. Otherwise nil and false is returned.
func convertToBulk(src interface{}) ([]byte, bool) {
switch v := src.(type) {
case []byte:
return v, true
case string:
return []byte(v), true
default:
return nil, false
}
}
var (
errScanSliceValue = errors.New("redigo.ScanSlice: dest must be non-nil pointer to a struct")
)

View File

@@ -1,5 +1,24 @@
version: "2"
linters:
disable:
- errcheck
- errcheck
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
paths:
- third_party$
- builtin$
- examples$
formatters:
enable:
- gofmt
- gofmt
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$

View File

@@ -1,6 +1,6 @@
[![Build Status](https://github.com/google/renameio/workflows/Test/badge.svg)](https://github.com/google/renameio/actions?query=workflow%3ATest)
[![PkgGoDev](https://pkg.go.dev/badge/github.com/google/renameio)](https://pkg.go.dev/github.com/google/renameio)
[![Go Report Card](https://goreportcard.com/badge/github.com/google/renameio)](https://goreportcard.com/report/github.com/google/renameio)
[![PkgGoDev](https://pkg.go.dev/badge/github.com/google/renameio/v2)](https://pkg.go.dev/github.com/google/renameio/v2)
[![Go Report Card](https://goreportcard.com/badge/github.com/google/renameio/v2)](https://goreportcard.com/report/github.com/google/renameio/v2)
The `renameio` Go package provides a way to atomically create or replace a file or
symbolic link.

View File

@@ -77,3 +77,12 @@ func WithExistingPermissions() Option {
c.attemptPermCopy = true
})
}
// WithReplaceOnClose causes PendingFile.Close() to actually call
// CloseAtomicallyReplace(). This means PendingFile implements io.Closer while
// maintaining atomicity per default.
func WithReplaceOnClose() Option {
return optionFunc(func(c *config) {
c.renameOnClose = true
})
}

View File

@@ -114,9 +114,10 @@ func tempDir(dir, dest string) string {
type PendingFile struct {
*os.File
path string
done bool
closed bool
path string
done bool
closed bool
replaceOnClose bool
}
// Cleanup is a no-op if CloseAtomicallyReplace succeeded, and otherwise closes
@@ -131,7 +132,7 @@ func (t *PendingFile) Cleanup() error {
// reporting, there is nothing the caller can recover here.
var closeErr error
if !t.closed {
closeErr = t.Close()
closeErr = t.File.Close()
}
if err := os.Remove(t.Name()); err != nil {
return err
@@ -159,7 +160,7 @@ func (t *PendingFile) CloseAtomicallyReplace() error {
return err
}
t.closed = true
if err := t.Close(); err != nil {
if err := t.File.Close(); err != nil {
return err
}
if err := os.Rename(t.Name(), t.path); err != nil {
@@ -169,6 +170,15 @@ func (t *PendingFile) CloseAtomicallyReplace() error {
return nil
}
// Close closes the file. By default it just calls Close() on the underlying file. For PendingFiles created with
// WithReplaceOnClose it calls CloseAtomicallyReplace() instead.
func (t *PendingFile) Close() error {
if t.replaceOnClose {
return t.CloseAtomicallyReplace()
}
return t.File.Close()
}
// TempFile creates a temporary file destined to atomically creating or
// replacing the destination file at path.
//
@@ -189,6 +199,7 @@ type config struct {
attemptPermCopy bool
ignoreUmask bool
chmod *os.FileMode
renameOnClose bool
}
// NewPendingFile creates a temporary file destined to atomically creating or
@@ -244,7 +255,7 @@ func NewPendingFile(path string, opts ...Option) (*PendingFile, error) {
}
}
return &PendingFile{File: f, path: cfg.path}, nil
return &PendingFile{File: f, path: cfg.path, replaceOnClose: cfg.renameOnClose}, nil
}
// Symlink wraps os.Symlink, replacing an existing symlink with the same name

View File

@@ -27,6 +27,16 @@ Use the links above for more information on each.
# changelog
* Oct 20, 2025 - [1.18.1](https://github.com/klauspost/compress/releases/tag/v1.18.1)
* zstd: Add simple zstd EncodeTo/DecodeTo functions https://github.com/klauspost/compress/pull/1079
* zstd: Fix incorrect buffer size in dictionary encodes https://github.com/klauspost/compress/pull/1059
* s2: check for cap, not len of buffer in EncodeBetter/Best by @vdarulis in https://github.com/klauspost/compress/pull/1080
* zlib: Avoiding extra allocation in zlib.reader.Reset by @travelpolicy in https://github.com/klauspost/compress/pull/1086
* gzhttp: remove redundant err check in zstdReader by @ryanfowler in https://github.com/klauspost/compress/pull/1090
* flate: Faster load+store https://github.com/klauspost/compress/pull/1104
* flate: Simplify matchlen https://github.com/klauspost/compress/pull/1101
* flate: Use exact sizes for huffman tables https://github.com/klauspost/compress/pull/1103
* Feb 19th, 2025 - [1.18.0](https://github.com/klauspost/compress/releases/tag/v1.18.0)
* Add unsafe little endian loaders https://github.com/klauspost/compress/pull/1036
* fix: check `r.err != nil` but return a nil value error `err` by @alingse in https://github.com/klauspost/compress/pull/1028
@@ -36,6 +46,9 @@ Use the links above for more information on each.
* flate: Fix matchlen L5+L6 https://github.com/klauspost/compress/pull/1049
* flate: Cleanup & reduce casts https://github.com/klauspost/compress/pull/1050
<details>
<summary>See changes to v1.17.x</summary>
* Oct 11th, 2024 - [1.17.11](https://github.com/klauspost/compress/releases/tag/v1.17.11)
* zstd: Fix extra CRC written with multiple Close calls https://github.com/klauspost/compress/pull/1017
* s2: Don't use stack for index tables https://github.com/klauspost/compress/pull/1014
@@ -102,7 +115,8 @@ https://github.com/klauspost/compress/pull/919 https://github.com/klauspost/comp
* s2: Do 2 overlapping match checks https://github.com/klauspost/compress/pull/839
* flate: Add amd64 assembly matchlen https://github.com/klauspost/compress/pull/837
* gzip: Copy bufio.Reader on Reset by @thatguystone in https://github.com/klauspost/compress/pull/860
</details>
<details>
<summary>See changes to v1.16.x</summary>
@@ -669,3 +683,4 @@ Here are other packages of good quality and pure Go (no cgo wrappers or autoconv
# license
This code is licensed under the same conditions as the original Go code. See LICENSE file.

View File

@@ -6,11 +6,12 @@
package flate
import (
"encoding/binary"
"errors"
"fmt"
"io"
"math"
"github.com/klauspost/compress/internal/le"
)
const (
@@ -234,12 +235,9 @@ func (d *compressor) fillWindow(b []byte) {
// Calculate 256 hashes at the time (more L1 cache hits)
loops := (n + 256 - minMatchLength) / 256
for j := 0; j < loops; j++ {
for j := range loops {
startindex := j * 256
end := startindex + 256 + minMatchLength - 1
if end > n {
end = n
}
end := min(startindex+256+minMatchLength-1, n)
tocheck := d.window[startindex:end]
dstSize := len(tocheck) - minMatchLength + 1
@@ -269,18 +267,12 @@ func (d *compressor) fillWindow(b []byte) {
// We only look at chainCount possibilities before giving up.
// pos = s.index, prevHead = s.chainHead-s.hashOffset, prevLength=minMatchLength-1, lookahead
func (d *compressor) findMatch(pos int, prevHead int, lookahead int) (length, offset int, ok bool) {
minMatchLook := maxMatchLength
if lookahead < minMatchLook {
minMatchLook = lookahead
}
minMatchLook := min(lookahead, maxMatchLength)
win := d.window[0 : pos+minMatchLook]
// We quit when we get a match that's at least nice long
nice := len(win) - pos
if d.nice < nice {
nice = d.nice
}
nice := min(d.nice, len(win)-pos)
// If we've got a match that's good enough, only look in 1/4 the chain.
tries := d.chain
@@ -288,10 +280,7 @@ func (d *compressor) findMatch(pos int, prevHead int, lookahead int) (length, of
wEnd := win[pos+length]
wPos := win[pos:]
minIndex := pos - windowSize
if minIndex < 0 {
minIndex = 0
}
minIndex := max(pos-windowSize, 0)
offset = 0
if d.chain < 100 {
@@ -374,7 +363,7 @@ func (d *compressor) writeStoredBlock(buf []byte) error {
// of the supplied slice.
// The caller must ensure that len(b) >= 4.
func hash4(b []byte) uint32 {
return hash4u(binary.LittleEndian.Uint32(b), hashBits)
return hash4u(le.Load32(b, 0), hashBits)
}
// hash4 returns the hash of u to fit in a hash table with h bits.
@@ -389,7 +378,7 @@ func bulkHash4(b []byte, dst []uint32) {
if len(b) < 4 {
return
}
hb := binary.LittleEndian.Uint32(b)
hb := le.Load32(b, 0)
dst[0] = hash4u(hb, hashBits)
end := len(b) - 4 + 1
@@ -432,7 +421,9 @@ func (d *compressor) deflateLazy() {
d.h = newHuffmanEncoder(maxFlateBlockTokens)
}
var tmp [256]uint16
for _, v := range d.window[s.index:d.windowEnd] {
toIndex := d.window[s.index:d.windowEnd]
toIndex = toIndex[:min(len(toIndex), maxFlateBlockTokens)]
for _, v := range toIndex {
tmp[v]++
}
d.h.generate(tmp[:], 15)
@@ -480,10 +471,7 @@ func (d *compressor) deflateLazy() {
prevOffset := s.offset
s.length = minMatchLength - 1
s.offset = 0
minIndex := s.index - windowSize
if minIndex < 0 {
minIndex = 0
}
minIndex := max(s.index-windowSize, 0)
if s.chainHead-s.hashOffset >= minIndex && lookahead > prevLength && prevLength < d.lazy {
if newLength, newOffset, ok := d.findMatch(s.index, s.chainHead-s.hashOffset, lookahead); ok {
@@ -503,10 +491,7 @@ func (d *compressor) deflateLazy() {
if prevLength < maxMatchLength-checkOff {
prevIndex := s.index - 1
if prevIndex+prevLength < s.maxInsertIndex {
end := lookahead
if lookahead > maxMatchLength+checkOff {
end = maxMatchLength + checkOff
}
end := min(lookahead, maxMatchLength+checkOff)
end += prevIndex
// Hash at match end.
@@ -603,15 +588,9 @@ func (d *compressor) deflateLazy() {
// table.
newIndex := s.index + prevLength - 1
// Calculate missing hashes
end := newIndex
if end > s.maxInsertIndex {
end = s.maxInsertIndex
}
end := min(newIndex, s.maxInsertIndex)
end += minMatchLength - 1
startindex := s.index + 1
if startindex > s.maxInsertIndex {
startindex = s.maxInsertIndex
}
startindex := min(s.index+1, s.maxInsertIndex)
tocheck := d.window[startindex:end]
dstSize := len(tocheck) - minMatchLength + 1
if dstSize > 0 {

View File

@@ -104,10 +104,7 @@ func (dd *dictDecoder) writeCopy(dist, length int) int {
dstBase := dd.wrPos
dstPos := dstBase
srcPos := dstPos - dist
endPos := dstPos + length
if endPos > len(dd.hist) {
endPos = len(dd.hist)
}
endPos := min(dstPos+length, len(dd.hist))
// Copy non-overlapping section after destination position.
//

View File

@@ -7,7 +7,6 @@ package flate
import (
"fmt"
"math/bits"
"github.com/klauspost/compress/internal/le"
)
@@ -151,29 +150,9 @@ func (e *fastGen) matchlen(s, t int, src []byte) int32 {
panic(fmt.Sprint(s, "-", t, "(", s-t, ") > maxMatchLength (", maxMatchOffset, ")"))
}
}
s1 := min(s+maxMatchLength-4, len(src))
left := s1 - s
n := int32(0)
for left >= 8 {
diff := le.Load64(src, s) ^ le.Load64(src, t)
if diff != 0 {
return n + int32(bits.TrailingZeros64(diff)>>3)
}
s += 8
t += 8
n += 8
left -= 8
}
a := src[s:s1]
a := src[s:min(s+maxMatchLength-4, len(src))]
b := src[t:]
for i := range a {
if a[i] != b[i] {
break
}
n++
}
return n
return int32(matchLen(a, b))
}
// matchlenLong will return the match length between offsets and t in src.
@@ -193,29 +172,7 @@ func (e *fastGen) matchlenLong(s, t int, src []byte) int32 {
panic(fmt.Sprint(s, "-", t, "(", s-t, ") > maxMatchLength (", maxMatchOffset, ")"))
}
}
// Extend the match to be as long as possible.
left := len(src) - s
n := int32(0)
for left >= 8 {
diff := le.Load64(src, s) ^ le.Load64(src, t)
if diff != 0 {
return n + int32(bits.TrailingZeros64(diff)>>3)
}
s += 8
t += 8
n += 8
left -= 8
}
a := src[s:]
b := src[t:]
for i := range a {
if a[i] != b[i] {
break
}
n++
}
return n
return int32(matchLen(src[s:], src[t:]))
}
// Reset the encoding table.

View File

@@ -211,7 +211,9 @@ func (w *huffmanBitWriter) flush() {
n++
}
w.bits = 0
w.write(w.bytes[:n])
if n > 0 {
w.write(w.bytes[:n])
}
w.nbytes = 0
}
@@ -303,10 +305,7 @@ func (w *huffmanBitWriter) generateCodegen(numLiterals int, numOffsets int, litE
w.codegenFreq[size]++
count--
for count >= 3 {
n := 6
if n > count {
n = count
}
n := min(6, count)
codegen[outIndex] = 16
outIndex++
codegen[outIndex] = uint8(n - 3)
@@ -316,10 +315,7 @@ func (w *huffmanBitWriter) generateCodegen(numLiterals int, numOffsets int, litE
}
} else {
for count >= 11 {
n := 138
if n > count {
n = count
}
n := min(138, count)
codegen[outIndex] = 18
outIndex++
codegen[outIndex] = uint8(n - 11)
@@ -438,8 +434,8 @@ func (w *huffmanBitWriter) writeOutBits() {
w.nbits -= 48
n := w.nbytes
// We over-write, but faster...
le.Store64(w.bytes[n:], bits)
// We overwrite, but faster...
le.Store64(w.bytes[:], n, bits)
n += 6
if n >= bufferFlushSize {
@@ -472,7 +468,7 @@ func (w *huffmanBitWriter) writeDynamicHeader(numLiterals int, numOffsets int, n
w.writeBits(int32(numOffsets-1), 5)
w.writeBits(int32(numCodegens-4), 4)
for i := 0; i < numCodegens; i++ {
for i := range numCodegens {
value := uint(w.codegenEncoding.codes[codegenOrder[i]].len())
w.writeBits(int32(value), 3)
}
@@ -650,7 +646,7 @@ func (w *huffmanBitWriter) writeBlockDynamic(tokens *tokens, eof bool, input []b
w.lastHeader = 0
}
numLiterals, numOffsets := w.indexTokens(tokens, !sync)
numLiterals, numOffsets := w.indexTokens(tokens, true)
extraBits := 0
ssize, storable := w.storedSize(input)
@@ -785,7 +781,7 @@ func (w *huffmanBitWriter) fillTokens() {
// literalFreq and offsetFreq, and generates literalEncoding
// and offsetEncoding.
// The number of literal and offset tokens is returned.
func (w *huffmanBitWriter) indexTokens(t *tokens, filled bool) (numLiterals, numOffsets int) {
func (w *huffmanBitWriter) indexTokens(t *tokens, alwaysEOB bool) (numLiterals, numOffsets int) {
//copy(w.literalFreq[:], t.litHist[:])
*(*[256]uint16)(w.literalFreq[:]) = t.litHist
//copy(w.literalFreq[256:], t.extraHist[:])
@@ -795,9 +791,10 @@ func (w *huffmanBitWriter) indexTokens(t *tokens, filled bool) (numLiterals, num
if t.n == 0 {
return
}
if filled {
return maxNumLit, maxNumDist
if alwaysEOB {
w.literalFreq[endBlockMarker] = 1
}
// get the number of literals
numLiterals = len(w.literalFreq)
for w.literalFreq[numLiterals-1] == 0 {
@@ -855,8 +852,7 @@ func (w *huffmanBitWriter) writeTokens(tokens []token, leCodes, oeCodes []hcode)
bits |= c.code64() << (nbits & 63)
nbits += c.len()
if nbits >= 48 {
le.Store64(w.bytes[nbytes:], bits)
//*(*uint64)(unsafe.Pointer(&w.bytes[nbytes])) = bits
le.Store64(w.bytes[:], nbytes, bits)
bits >>= 48
nbits -= 48
nbytes += 6
@@ -883,8 +879,7 @@ func (w *huffmanBitWriter) writeTokens(tokens []token, leCodes, oeCodes []hcode)
bits |= c.code64() << (nbits & 63)
nbits += c.len()
if nbits >= 48 {
le.Store64(w.bytes[nbytes:], bits)
//*(*uint64)(unsafe.Pointer(&w.bytes[nbytes])) = bits
le.Store64(w.bytes[:], nbytes, bits)
bits >>= 48
nbits -= 48
nbytes += 6
@@ -906,8 +901,7 @@ func (w *huffmanBitWriter) writeTokens(tokens []token, leCodes, oeCodes []hcode)
bits |= uint64(extraLength) << (nbits & 63)
nbits += extraLengthBits
if nbits >= 48 {
le.Store64(w.bytes[nbytes:], bits)
//*(*uint64)(unsafe.Pointer(&w.bytes[nbytes])) = bits
le.Store64(w.bytes[:], nbytes, bits)
bits >>= 48
nbits -= 48
nbytes += 6
@@ -932,8 +926,7 @@ func (w *huffmanBitWriter) writeTokens(tokens []token, leCodes, oeCodes []hcode)
bits |= c.code64() << (nbits & 63)
nbits += c.len()
if nbits >= 48 {
le.Store64(w.bytes[nbytes:], bits)
//*(*uint64)(unsafe.Pointer(&w.bytes[nbytes])) = bits
le.Store64(w.bytes[:], nbytes, bits)
bits >>= 48
nbits -= 48
nbytes += 6
@@ -954,8 +947,7 @@ func (w *huffmanBitWriter) writeTokens(tokens []token, leCodes, oeCodes []hcode)
bits |= uint64((offset-(offsetComb>>8))&matchOffsetOnlyMask) << (nbits & 63)
nbits += uint8(offsetComb)
if nbits >= 48 {
le.Store64(w.bytes[nbytes:], bits)
//*(*uint64)(unsafe.Pointer(&w.bytes[nbytes])) = bits
le.Store64(w.bytes[:], nbytes, bits)
bits >>= 48
nbits -= 48
nbytes += 6
@@ -1108,7 +1100,7 @@ func (w *huffmanBitWriter) writeBlockHuff(eof bool, input []byte, sync bool) {
// We must have at least 48 bits free.
if nbits >= 8 {
n := nbits >> 3
le.Store64(w.bytes[nbytes:], bits)
le.Store64(w.bytes[:], nbytes, bits)
bits >>= (n * 8) & 63
nbits -= n * 8
nbytes += n
@@ -1137,8 +1129,7 @@ func (w *huffmanBitWriter) writeBlockHuff(eof bool, input []byte, sync bool) {
// Remaining...
for _, t := range input {
if nbits >= 48 {
le.Store64(w.bytes[nbytes:], bits)
//*(*uint64)(unsafe.Pointer(&w.bytes[nbytes])) = bits
le.Store64(w.bytes[:], nbytes, bits)
bits >>= 48
nbits -= 48
nbytes += 6

View File

@@ -91,7 +91,7 @@ func generateFixedLiteralEncoding() *huffmanEncoder {
h := newHuffmanEncoder(literalCount)
codes := h.codes
var ch uint16
for ch = 0; ch < literalCount; ch++ {
for ch = range uint16(literalCount) {
var bits uint16
var size uint8
switch {

View File

@@ -485,7 +485,7 @@ func (f *decompressor) readHuffman() error {
f.nb -= 5 + 5 + 4
// (HCLEN+4)*3 bits: code lengths in the magic codeOrder order.
for i := 0; i < nclen; i++ {
for i := range nclen {
for f.nb < 3 {
if err := f.moreBits(); err != nil {
return err
@@ -776,7 +776,7 @@ func fixedHuffmanDecoderInit() {
fixedOnce.Do(func() {
// These come from the RFC section 3.2.6.
var bits [288]int
for i := 0; i < 144; i++ {
for i := range 144 {
bits[i] = 8
}
for i := 144; i < 256; i++ {

View File

@@ -677,10 +677,7 @@ func (e *fastEncL5Window) matchlen(s, t int32, src []byte) int32 {
panic(fmt.Sprint(s, "-", t, "(", s-t, ") > maxMatchLength (", maxMatchOffset, ")"))
}
}
s1 := int(s) + maxMatchLength - 4
if s1 > len(src) {
s1 = len(src)
}
s1 := min(int(s)+maxMatchLength-4, len(src))
// Extend the match to be as long as possible.
return int32(matchLen(src[s:s1], src[t:]))

View File

@@ -56,18 +56,24 @@ func NewStatelessWriter(dst io.Writer) io.WriteCloser {
// bitWriterPool contains bit writers that can be reused.
var bitWriterPool = sync.Pool{
New: func() interface{} {
New: func() any {
return newHuffmanBitWriter(nil)
},
}
// tokensPool contains tokens struct objects that can be reused
var tokensPool = sync.Pool{
New: func() any {
return &tokens{}
},
}
// StatelessDeflate allows compressing directly to a Writer without retaining state.
// When returning everything will be flushed.
// Up to 8KB of an optional dictionary can be given which is presumed to precede the block.
// Longer dictionaries will be truncated and will still produce valid output.
// Sending nil dictionary is perfectly fine.
func StatelessDeflate(out io.Writer, in []byte, eof bool, dict []byte) error {
var dst tokens
bw := bitWriterPool.Get().(*huffmanBitWriter)
bw.reset(out)
defer func() {
@@ -91,6 +97,12 @@ func StatelessDeflate(out io.Writer, in []byte, eof bool, dict []byte) error {
// For subsequent loops, keep shallow dict reference to avoid alloc+copy.
var inDict []byte
dst := tokensPool.Get().(*tokens)
dst.Reset()
defer func() {
tokensPool.Put(dst)
}()
for len(in) > 0 {
todo := in
if len(inDict) > 0 {
@@ -113,9 +125,9 @@ func StatelessDeflate(out io.Writer, in []byte, eof bool, dict []byte) error {
}
// Compress
if len(inDict) == 0 {
statelessEnc(&dst, todo, int16(len(dict)))
statelessEnc(dst, todo, int16(len(dict)))
} else {
statelessEnc(&dst, inDict[:maxStatelessDict+len(todo)], maxStatelessDict)
statelessEnc(dst, inDict[:maxStatelessDict+len(todo)], maxStatelessDict)
}
isEof := eof && len(in) == 0
@@ -129,7 +141,7 @@ func StatelessDeflate(out io.Writer, in []byte, eof bool, dict []byte) error {
// If we removed less than 1/16th, huffman compress the block.
bw.writeBlockHuff(isEof, uncompressed, len(in) == 0)
} else {
bw.writeBlockDynamic(&dst, isEof, uncompressed, len(in) == 0)
bw.writeBlockDynamic(dst, isEof, uncompressed, len(in) == 0)
}
if len(in) > 0 {
// Retain a dict if we have more
@@ -184,7 +196,7 @@ func statelessEnc(dst *tokens, src []byte, startAt int16) {
// Index until startAt
if startAt > 0 {
cv := load3232(src, 0)
for i := int16(0); i < startAt; i++ {
for i := range startAt {
table[hashSL(cv)] = tableEntry{offset: i}
cv = (cv >> 8) | (uint32(src[i+4]) << 24)
}

View File

@@ -143,7 +143,7 @@ func (b *bitWriter) flush32() {
// flushAlign will flush remaining full bytes and align to next byte boundary.
func (b *bitWriter) flushAlign() {
nbBytes := (b.nBits + 7) >> 3
for i := uint8(0); i < nbBytes; i++ {
for i := range nbBytes {
b.out = append(b.out, byte(b.bitContainer>>(i*8)))
}
b.nBits = 0

View File

@@ -396,7 +396,7 @@ func (s *Scratch) buildCTable() error {
if v > largeLimit {
s.zeroBits = true
}
for nbOccurrences := int16(0); nbOccurrences < v; nbOccurrences++ {
for range v {
tableSymbol[position] = symbol
position = (position + step) & tableMask
for position > highThreshold {

View File

@@ -85,7 +85,7 @@ func (b *bitWriter) flush32() {
// flushAlign will flush remaining full bytes and align to next byte boundary.
func (b *bitWriter) flushAlign() {
nbBytes := (b.nBits + 7) >> 3
for i := uint8(0); i < nbBytes; i++ {
for i := range nbBytes {
b.out = append(b.out, byte(b.bitContainer>>(i*8)))
}
b.nBits = 0

View File

@@ -276,7 +276,7 @@ func (s *Scratch) compress4X(src []byte) ([]byte, error) {
offsetIdx := len(s.Out)
s.Out = append(s.Out, sixZeros[:]...)
for i := 0; i < 4; i++ {
for i := range 4 {
toDo := src
if len(toDo) > segmentSize {
toDo = toDo[:segmentSize]
@@ -312,7 +312,7 @@ func (s *Scratch) compress4Xp(src []byte) ([]byte, error) {
segmentSize := (len(src) + 3) / 4
var wg sync.WaitGroup
wg.Add(4)
for i := 0; i < 4; i++ {
for i := range 4 {
toDo := src
if len(toDo) > segmentSize {
toDo = toDo[:segmentSize]
@@ -326,7 +326,7 @@ func (s *Scratch) compress4Xp(src []byte) ([]byte, error) {
}(i)
}
wg.Wait()
for i := 0; i < 4; i++ {
for i := range 4 {
o := s.tmpOut[i]
if len(o) > math.MaxUint16 {
// We cannot store the size in the jump table

View File

@@ -626,7 +626,7 @@ func (d *Decoder) decompress4X8bit(dst, src []byte) ([]byte, error) {
var br [4]bitReaderBytes
start := 6
for i := 0; i < 3; i++ {
for i := range 3 {
length := int(src[i*2]) | (int(src[i*2+1]) << 8)
if start+length >= len(src) {
return nil, errors.New("truncated input (or invalid offset)")
@@ -798,10 +798,7 @@ func (d *Decoder) decompress4X8bit(dst, src []byte) ([]byte, error) {
remainBytes := dstEvery - (decoded / 4)
for i := range br {
offset := dstEvery * i
endsAt := offset + remainBytes
if endsAt > len(out) {
endsAt = len(out)
}
endsAt := min(offset+remainBytes, len(out))
br := &br[i]
bitsLeft := br.remaining()
for bitsLeft > 0 {
@@ -864,7 +861,7 @@ func (d *Decoder) decompress4X8bit(dst, src []byte) ([]byte, error) {
func (d *Decoder) decompress4X8bitExactly(dst, src []byte) ([]byte, error) {
var br [4]bitReaderBytes
start := 6
for i := 0; i < 3; i++ {
for i := range 3 {
length := int(src[i*2]) | (int(src[i*2+1]) << 8)
if start+length >= len(src) {
return nil, errors.New("truncated input (or invalid offset)")
@@ -1035,10 +1032,7 @@ func (d *Decoder) decompress4X8bitExactly(dst, src []byte) ([]byte, error) {
remainBytes := dstEvery - (decoded / 4)
for i := range br {
offset := dstEvery * i
endsAt := offset + remainBytes
if endsAt > len(out) {
endsAt = len(out)
}
endsAt := min(offset+remainBytes, len(out))
br := &br[i]
bitsLeft := br.remaining()
for bitsLeft > 0 {

View File

@@ -58,7 +58,7 @@ func (d *Decoder) Decompress4X(dst, src []byte) ([]byte, error) {
var br [4]bitReaderShifted
// Decode "jump table"
start := 6
for i := 0; i < 3; i++ {
for i := range 3 {
length := int(src[i*2]) | (int(src[i*2+1]) << 8)
if start+length >= len(src) {
return nil, errors.New("truncated input (or invalid offset)")
@@ -109,10 +109,7 @@ func (d *Decoder) Decompress4X(dst, src []byte) ([]byte, error) {
remainBytes := dstEvery - (decoded / 4)
for i := range br {
offset := dstEvery * i
endsAt := offset + remainBytes
if endsAt > len(out) {
endsAt = len(out)
}
endsAt := min(offset+remainBytes, len(out))
br := &br[i]
bitsLeft := br.remaining()
for bitsLeft > 0 {

View File

@@ -201,7 +201,7 @@ func (c cTable) write(s *Scratch) error {
for i := range hist[:16] {
hist[i] = 0
}
for n := uint8(0); n < maxSymbolValue; n++ {
for n := range maxSymbolValue {
v := bitsToWeight[c[n].nBits] & 15
huffWeight[n] = v
hist[v]++
@@ -271,7 +271,7 @@ func (c cTable) estTableSize(s *Scratch) (sz int, err error) {
for i := range hist[:16] {
hist[i] = 0
}
for n := uint8(0); n < maxSymbolValue; n++ {
for n := range maxSymbolValue {
v := bitsToWeight[c[n].nBits] & 15
huffWeight[n] = v
hist[v]++

View File

@@ -37,6 +37,6 @@ func Store32(b []byte, v uint32) {
}
// Store64 will store v at b.
func Store64(b []byte, v uint64) {
binary.LittleEndian.PutUint64(b, v)
func Store64[I Indexer](b []byte, i I, v uint64) {
binary.LittleEndian.PutUint64(b[i:], v)
}

View File

@@ -38,18 +38,15 @@ func Load64[I Indexer](b []byte, i I) uint64 {
// Store16 will store v at b.
func Store16(b []byte, v uint16) {
//binary.LittleEndian.PutUint16(b, v)
*(*uint16)(unsafe.Pointer(unsafe.SliceData(b))) = v
}
// Store32 will store v at b.
func Store32(b []byte, v uint32) {
//binary.LittleEndian.PutUint32(b, v)
*(*uint32)(unsafe.Pointer(unsafe.SliceData(b))) = v
}
// Store64 will store v at b.
func Store64(b []byte, v uint64) {
//binary.LittleEndian.PutUint64(b, v)
*(*uint64)(unsafe.Pointer(unsafe.SliceData(b))) = v
// Store64 will store v at b[i:].
func Store64[I Indexer](b []byte, i I, v uint64) {
*(*uint64)(unsafe.Add(unsafe.Pointer(unsafe.SliceData(b)), i)) = v
}

View File

@@ -209,7 +209,7 @@ func (r *Reader) fill() error {
if !r.readFull(r.buf[:len(magicBody)], false) {
return r.err
}
for i := 0; i < len(magicBody); i++ {
for i := range len(magicBody) {
if r.buf[i] != magicBody[i] {
r.err = ErrCorrupt
return r.err

View File

@@ -20,8 +20,10 @@ import (
func Encode(dst, src []byte) []byte {
if n := MaxEncodedLen(len(src)); n < 0 {
panic(ErrTooLarge)
} else if len(dst) < n {
} else if cap(dst) < n {
dst = make([]byte, n)
} else {
dst = dst[:n]
}
// The block starts with the varint-encoded length of the decompressed bytes.

View File

@@ -1,3 +1,17 @@
# MinLZ
I have taken the experiences from this library and created a backwards compatible compression package called MinLZ.
That package will seamlessly decode S2 content, making the transition from this package fairly trivial.
There are many improvements to pretty much all aspects of S2 since we have "broken free" of the Snappy format specification.
You can read a writeup on [Design and Improvements over S2](https://gist.github.com/klauspost/a25b66198cdbdf7b5b224f670c894ed5).
The only aspect not covered is custom dictionary encoding. While I do intend to fix errors in this package,
I do not expect to make significant improvements, since I consider MinLZ a better basis for going forward.
See https://github.com/minio/minlz for all details.
# S2 Compression
S2 is an extension of [Snappy](https://github.com/google/snappy).

View File

@@ -117,8 +117,10 @@ func EstimateBlockSize(src []byte) (d int) {
func EncodeBetter(dst, src []byte) []byte {
if n := MaxEncodedLen(len(src)); n < 0 {
panic(ErrTooLarge)
} else if len(dst) < n {
} else if cap(dst) < n {
dst = make([]byte, n)
} else {
dst = dst[:n]
}
// The block starts with the varint-encoded length of the decompressed bytes.
@@ -159,8 +161,10 @@ func EncodeBetter(dst, src []byte) []byte {
func EncodeBest(dst, src []byte) []byte {
if n := MaxEncodedLen(len(src)); n < 0 {
panic(ErrTooLarge)
} else if len(dst) < n {
} else if cap(dst) < n {
dst = make([]byte, n)
} else {
dst = dst[:n]
}
// The block starts with the varint-encoded length of the decompressed bytes.

View File

@@ -903,10 +903,7 @@ func encodeBlockDictGo(dst, src []byte, dict *Dict) (d int) {
// sLimit is when to stop looking for offset/length copies. The inputMargin
// lets us use a fast path for emitLiteral in the main loop, while we are
// looking for copies.
sLimit := len(src) - inputMargin
if sLimit > MaxDictSrcOffset-maxAhead {
sLimit = MaxDictSrcOffset - maxAhead
}
sLimit := min(len(src)-inputMargin, MaxDictSrcOffset-maxAhead)
// Bail if we can't compress to at least this.
dstLimit := len(src) - len(src)>>5 - 5

View File

@@ -42,10 +42,7 @@ func encodeBlockBest(dst, src []byte, dict *Dict) (d int) {
if len(src) < minNonLiteralBlockSize {
return 0
}
sLimitDict := len(src) - inputMargin
if sLimitDict > MaxDictSrcOffset-inputMargin {
sLimitDict = MaxDictSrcOffset - inputMargin
}
sLimitDict := min(len(src)-inputMargin, MaxDictSrcOffset-inputMargin)
var lTable [maxLTableSize]uint64
var sTable [maxSTableSize]uint64

View File

@@ -914,10 +914,7 @@ func encodeBlockBetterDict(dst, src []byte, dict *Dict) (d int) {
debug = false
)
sLimit := len(src) - inputMargin
if sLimit > MaxDictSrcOffset-maxAhead {
sLimit = MaxDictSrcOffset - maxAhead
}
sLimit := min(len(src)-inputMargin, MaxDictSrcOffset-maxAhead)
if len(src) < minNonLiteralBlockSize {
return 0
}

View File

@@ -72,7 +72,7 @@ func (i *Index) add(compressedOffset, uncompressedOffset int64) error {
return fmt.Errorf("internal error: Earlier uncompressed received (%d > %d)", latest.uncompressedOffset, uncompressedOffset)
}
if latest.compressedOffset > compressedOffset {
return fmt.Errorf("internal error: Earlier compressed received (%d > %d)", latest.uncompressedOffset, uncompressedOffset)
return fmt.Errorf("internal error: Earlier compressed received (%d > %d)", latest.compressedOffset, compressedOffset)
}
if latest.uncompressedOffset+minIndexDist > uncompressedOffset {
// Only add entry if distance is large enough.

View File

@@ -1046,7 +1046,7 @@ func (r *Reader) ReadByte() (byte, error) {
return c, nil
}
var tmp [1]byte
for i := 0; i < 10; i++ {
for range 10 {
n, err := r.Read(tmp[:])
if err != nil {
return 0, err

View File

@@ -47,7 +47,7 @@ func NewWriter(w io.Writer, opts ...WriterOption) *Writer {
w2.obufLen = obufHeaderLen + MaxEncodedLen(w2.blockSize)
w2.paramsOK = true
w2.ibuf = make([]byte, 0, w2.blockSize)
w2.buffers.New = func() interface{} {
w2.buffers.New = func() any {
return make([]byte, w2.obufLen)
}
w2.Reset(w)

View File

@@ -88,7 +88,7 @@ func (b *bitWriter) flush32() {
// flushAlign will flush remaining full bytes and align to next byte boundary.
func (b *bitWriter) flushAlign() {
nbBytes := (b.nBits + 7) >> 3
for i := uint8(0); i < nbBytes; i++ {
for i := range nbBytes {
b.out = append(b.out, byte(b.bitContainer>>(i*8)))
}
b.nBits = 0

View File

@@ -54,11 +54,11 @@ const (
)
var (
huffDecoderPool = sync.Pool{New: func() interface{} {
huffDecoderPool = sync.Pool{New: func() any {
return &huff0.Scratch{}
}}
fseDecoderPool = sync.Pool{New: func() interface{} {
fseDecoderPool = sync.Pool{New: func() any {
return &fseDecoder{}
}}
)
@@ -553,7 +553,7 @@ func (b *blockDec) prepareSequences(in []byte, hist *history) (err error) {
if compMode&3 != 0 {
return errors.New("corrupt block: reserved bits not zero")
}
for i := uint(0); i < 3; i++ {
for i := range uint(3) {
mode := seqCompMode((compMode >> (6 - i*2)) & 3)
if debugDecoder {
println("Table", tableIndex(i), "is", mode)

View File

@@ -373,11 +373,9 @@ func (d *Decoder) DecodeAll(input, dst []byte) ([]byte, error) {
if cap(dst) == 0 && !d.o.limitToCap {
// Allocate len(input) * 2 by default if nothing is provided
// and we didn't get frame content size.
size := len(input) * 2
// Cap to 1 MB.
if size > 1<<20 {
size = 1 << 20
}
size := min(
// Cap to 1 MB.
len(input)*2, 1<<20)
if uint64(size) > d.o.maxDecodedSize {
size = int(d.o.maxDecodedSize)
}

Some files were not shown because too many files have changed in this diff Show More