Compare commits

..
57 Commits
Author SHA1 Message Date
renovate-bot 5b42b3d306 chore(deps): update golang docker tag 2026-10-09 17:01:20 +00:00
renovate-bot d99e86fb0e chore(deps): update module golang.org/x/term to v0.47.0 2026-10-09 16:01:52 +00:00
renovate-bot ade646abbc chore(deps): update golang docker tag 2026-10-09 14:01:30 +00:00
renovate-bot 80b3b90905 chore(deps): update module golang.org/x/sys to v0.49.0 2026-10-09 13:01:52 +00:00
renovate-bot fab3f0b97b chore(deps): update golang docker tag 2026-10-04 16:01:27 +00:00
renovate-bot 3819ee38f5 chore(deps): update module github.com/go-git/go-git/v5 to v5.19.3 2026-10-04 15:02:06 +00:00
renovate-bot 74261a7a01 chore(deps): update otel/weaver docker tag to v0.27.0 2026-10-04 01:01:15 +00:00
renovate-bot ca39ff6e29 chore(deps): update nginx docker tag to v1.31.6 2026-09-15 23:01:13 +00:00
renovate-bot 5d00b3c932 chore(deps): update golang docker tag 2026-09-08 19:01:07 +00:00
renovate-bot a380b5e8ae chore(deps): update module golang.org/x/term to v0.46.0 2026-09-08 18:03:14 +00:00
renovate-bot 9ff5024a58 chore(deps): update golang docker tag 2026-09-08 15:04:36 +00:00
renovate-bot d6dd082e59 chore(deps): update module golang.org/x/sys to v0.48.0 2026-09-08 14:02:32 +00:00
Weblate 0531f30590 chore: update translation files
Updated by "Update PO files to match POT (msgmerge)" add-on in Weblate.

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/
2026-09-07 14:34:36 +00:00
moritz 27ca0cd21e test(app new): cover a commit that only exists on another branch 2026-09-07 14:34:27 +00:00
moritz 920a898955 fix(git): clone every branch so commits outside the default branch resolve 2026-09-07 14:34:27 +00:00
sixsmith 7cbc974763 chore: mark vendor directory as generated 2026-09-03 05:30:34 +00:00
renovate-bot c71b764546 chore(deps): update otel/weaver docker tag to v0.26.1 2026-09-03 02:01:10 +00:00
renovate-bot 5db8430a73 chore(deps): update nginx docker tag to v1.31.5 2026-09-02 23:01:08 +00:00
renovate-bot 091c4a2ac1 chore(deps): update otel/weaver docker tag to v0.26.0 2026-09-02 19:01:09 +00:00
ChasquiLabo 8e935d08a6 chore: translation using Weblate (Spanish)
Currently translated at 90.6% (1046 of 1154 strings)

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/
2026-09-01 22:35:20 +00:00
ChasquiLabo b959c55852 chore: translation using Weblate (Spanish)
Currently translated at 90.6% (1046 of 1154 strings)

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/
2026-09-01 21:54:01 +00:00
ChasquiLabo bab9b21882 chore: translation using Weblate (Spanish)
Currently translated at 90.6% (1046 of 1154 strings)

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/
2026-09-01 21:42:51 +00:00
ChasquiLabo 703fbf5c8a chore: translation using Weblate (Spanish)
Currently translated at 90.6% (1046 of 1154 strings)

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/
2026-09-01 21:41:22 +00:00
ChasquiLabo aaaf855a6e chore: translation using Weblate (Spanish)
Currently translated at 90.6% (1046 of 1154 strings)

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/
2026-09-01 21:39:07 +00:00
ChasquiLabo a6666be477 chore: translation using Weblate (Spanish)
Currently translated at 90.6% (1046 of 1154 strings)

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/
2026-09-01 19:30:19 +00:00
ChasquiLabo ba90b04624 chore: translation using Weblate (Spanish)
Currently translated at 90.6% (1046 of 1154 strings)

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/
2026-09-01 19:03:00 +00:00
ChasquiLabo debbdde5d1 chore: translation using Weblate (Spanish)
Currently translated at 90.6% (1046 of 1154 strings)

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/
2026-09-01 19:00:21 +00:00
ChasquiLabo 48f62f541f chore: translation using Weblate (Spanish)
Currently translated at 90.6% (1046 of 1154 strings)

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/
2026-09-01 18:56:40 +00:00
ChasquiLabo e6296e4ce4 chore: translation using Weblate (Spanish)
Currently translated at 90.6% (1046 of 1154 strings)

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/
2026-09-01 18:53:08 +00:00
ChasquiLabo 423650e8b5 chore: translation using Weblate (Spanish)
Currently translated at 90.6% (1046 of 1154 strings)

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/
2026-09-01 18:50:14 +00:00
ChasquiLabo d0c42e37a7 chore: translation using Weblate (Spanish)
Currently translated at 90.6% (1046 of 1154 strings)

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/
2026-09-01 16:56:31 +00:00
ChasquiLabo 6afd17f1ed chore: translation using Weblate (Spanish)
Currently translated at 90.6% (1046 of 1154 strings)

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/
2026-09-01 16:53:56 +00:00
ChasquiLabo cb0eec2203 chore: translation using Weblate (Spanish)
Currently translated at 90.6% (1046 of 1154 strings)

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/
2026-09-01 16:52:45 +00:00
ChasquiLabo f5900b726b chore: translation using Weblate (Spanish)
Currently translated at 90.6% (1046 of 1154 strings)

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/
2026-09-01 16:50:52 +00:00
ChasquiLabo 0d9e777c18 chore: translation using Weblate (Spanish)
Currently translated at 90.6% (1046 of 1154 strings)

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/
2026-09-01 16:39:47 +00:00
ChasquiLabo f5cc61c211 chore: translation using Weblate (Spanish)
Currently translated at 90.6% (1046 of 1154 strings)

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/
2026-09-01 16:27:24 +00:00
ChasquiLabo 786ba43b83 chore: translation using Weblate (Spanish)
Currently translated at 90.6% (1046 of 1154 strings)

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/
2026-09-01 16:24:37 +00:00
ChasquiLabo ee80cfa27f chore: translation using Weblate (Spanish)
Currently translated at 90.6% (1046 of 1154 strings)

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/
2026-09-01 16:00:49 +00:00
ChasquiLabo bbaca64d36 chore: translation using Weblate (Spanish)
Currently translated at 90.6% (1046 of 1154 strings)

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/
2026-09-01 15:56:33 +00:00
ChasquiLabo b608ee3985 chore: translation using Weblate (Spanish)
Currently translated at 90.6% (1046 of 1154 strings)

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/
2026-09-01 15:44:56 +00:00
ChasquiLabo 5e72e28ba6 chore: translation using Weblate (Spanish)
Currently translated at 90.6% (1046 of 1154 strings)

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/
2026-09-01 15:37:17 +00:00
ChasquiLabo 84b18c1a7b chore: translation using Weblate (Spanish)
Currently translated at 90.6% (1046 of 1154 strings)

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/
2026-09-01 15:26:49 +00:00
ChasquiLabo 249851a556 chore: translation using Weblate (Spanish)
Currently translated at 90.6% (1046 of 1154 strings)

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/
2026-09-01 14:16:43 +00:00
ChasquiLabo 344b4c8d3d chore: translation using Weblate (Spanish)
Currently translated at 90.6% (1046 of 1154 strings)

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/
2026-09-01 13:12:53 +00:00
renovate-bot 0dd314b53c chore(deps): update nginx docker tag to v1.31.4 2026-08-19 21:01:10 +00:00
renovate-bot e9f51526f6 chore(deps): update golang docker tag 2026-08-19 20:01:03 +00:00
renovate-bot ae2080fd9d chore(deps): update golang docker tag 2026-08-19 15:01:28 +00:00
renovate-bot 8f6ebac2ce chore(deps): update module github.com/stretchr/testify to v1.12.1 2026-08-19 14:01:40 +00:00
renovate-bot 23d67677c6 chore(deps): update golang docker tag 2026-08-17 10:01:49 +00:00
renovate-bot 00d64556bc chore(deps): update module github.com/stretchr/testify to v1.12.0 2026-08-17 09:04:16 +00:00
ChasquiLabo 70c0accf7f chore: translation using Weblate (Spanish)
Currently translated at 90.6% (1046 of 1154 strings)

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/
2026-08-11 06:51:47 +00:00
ChasquiLabo 82ae4cb10f chore: translation using Weblate (Spanish)
Currently translated at 90.6% (1046 of 1154 strings)

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/
2026-08-10 22:00:01 +00:00
ChasquiLabo 99d3cc87b2 chore: translation using Weblate (Spanish)
Currently translated at 90.6% (1046 of 1154 strings)

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/
2026-08-10 02:00:46 +00:00
ChasquiLabo 232d6f39e4 chore: translation using Weblate (Spanish)
Currently translated at 90.6% (1046 of 1154 strings)

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/
2026-08-09 21:49:50 +00:00
renovate-bot 26089f0886 chore(deps): update golang docker tag 2026-07-29 14:00:59 +00:00
renovate-bot a1de95a8ce chore(deps): update module github.com/go-git/go-git/v5 to v5.19.2 2026-07-29 12:02:00 +00:00
renovate-bot f4861bf3c1 chore(deps): update otel/weaver docker tag to v0.25.1 2026-07-29 02:00:47 +00:00
249 changed files with 14692 additions and 29953 deletions
+3 -3
View File
@@ -3,12 +3,12 @@ kind: pipeline
name: coopcloud.tech/abra
steps:
- name: make check
image: golang:1.26
image: golang:1.27
commands:
- make check
- name: xgettext-go
image: golang:1.26
image: golang:1.27
environment:
GOPRIVATE: coopcloud.tech
commands:
@@ -43,7 +43,7 @@ steps:
- tag
- name: make test
image: golang:1.26
image: golang:1.27
environment:
ABRA_DIR: /root/.abra_test
commands:
+1
View File
@@ -0,0 +1 @@
vendor/** linguist-generated=true
+1 -1
View File
@@ -1,5 +1,5 @@
# Build image
FROM golang:1.26-alpine AS build
FROM golang:1.27-alpine AS build
ENV GOPRIVATE=coopcloud.tech
+10 -13
View File
@@ -13,14 +13,14 @@ require (
github.com/docker/cli v28.4.0+incompatible
github.com/docker/docker v28.5.2+incompatible
github.com/docker/go-units v0.5.0
github.com/go-git/go-git/v5 v5.19.1
github.com/go-git/go-git/v5 v5.19.3
github.com/google/go-cmp v0.7.0
github.com/leonelquinteros/gotext v1.7.2
github.com/moby/sys/signal v0.7.1
github.com/moby/term v0.5.2
github.com/pkg/errors v0.9.1
github.com/schollz/progressbar/v3 v3.19.1
golang.org/x/term v0.45.0
golang.org/x/term v0.47.0
gopkg.in/yaml.v3 v3.0.1
gotest.tools/v3 v3.5.2
)
@@ -50,7 +50,6 @@ require (
github.com/containerd/platforms v0.2.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
github.com/cyphar/filepath-securejoin v0.6.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/go-connections v0.6.0 // indirect
github.com/docker/go-metrics v0.0.1 // indirect
@@ -60,7 +59,7 @@ require (
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.9.0 // indirect
github.com/go-git/go-billy/v5 v5.9.2 // indirect
github.com/go-logfmt/logfmt v0.6.1 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
@@ -74,7 +73,6 @@ require (
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/kevinburke/ssh_config v1.6.0 // indirect
github.com/klauspost/compress v1.18.5 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/lucasb-eyer/go-colorful v1.4.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.22 // indirect
@@ -96,8 +94,7 @@ require (
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/runc v1.1.13 // indirect
github.com/opencontainers/runtime-spec v1.1.0 // indirect
github.com/pjbgf/sha1cd v0.6.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/pjbgf/sha1cd v0.7.0 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.67.5 // indirect
github.com/prometheus/procfs v0.20.1 // indirect
@@ -123,11 +120,11 @@ require (
go.opentelemetry.io/otel/trace v1.42.0 // indirect
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
go.yaml.in/yaml/v2 v2.4.4 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/crypto v0.50.0 // indirect
go.yaml.in/yaml/v3 v3.0.5 // indirect
golang.org/x/crypto v0.56.0 // indirect
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f // indirect
golang.org/x/net v0.53.0 // indirect
golang.org/x/text v0.36.0 // indirect
golang.org/x/net v0.57.0 // indirect
golang.org/x/text v0.41.0 // indirect
golang.org/x/time v0.15.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect
@@ -152,10 +149,10 @@ require (
github.com/prometheus/client_golang v1.23.2 // indirect
github.com/sergi/go-diff v1.4.0 // indirect
github.com/spf13/cobra v1.10.1
github.com/stretchr/testify v1.11.1
github.com/stretchr/testify v1.12.1
github.com/theupdateframework/notary v0.7.0 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
golang.org/x/sys v0.47.0
golang.org/x/sys v0.49.0
)
replace github.com/docker/cli v28.4.0+incompatible => git.coopcloud.tech/toolshed/docker-cli v28.5.3-0.20260202112816-30df2d0b3a00+incompatible
+20 -23
View File
@@ -306,7 +306,6 @@ github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW
github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8=
github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decentral1se/cobra v1.10.2 h1:MZ8Ifi/jRels9sZrpSccDbUlK++3b2HlBODfv0Bh6x0=
github.com/decentral1se/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
@@ -389,12 +388,12 @@ github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
github.com/go-git/go-billy/v5 v5.9.0 h1:jItGXszUDRtR/AlferWPTMN4j38BQ88XnXKbilmmBPA=
github.com/go-git/go-billy/v5 v5.9.0/go.mod h1:jCnQMLj9eUgGU7+ludSTYoZL/GGmii14RxKFj7ROgHw=
github.com/go-git/go-billy/v5 v5.9.2 h1:OXFSRyz4g20upsGDJgQG9Bak1l/ZEv8GHVYB52O71sE=
github.com/go-git/go-billy/v5 v5.9.2/go.mod h1:ExsU+jcGwXTBOnyilvAnEM1wug1IxHr4yP2ZXsNRtV0=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
github.com/go-git/go-git/v5 v5.19.1 h1:nX27AnaU43/K5bKktKwgBmR9lawoYVe1Ckg0rgzzN00=
github.com/go-git/go-git/v5 v5.19.1/go.mod h1:Pb1v0c7/g8aGQJwx9Us09W85yGoyvSwuhEGMH7zjDKQ=
github.com/go-git/go-git/v5 v5.19.3 h1:qHttbZ+Am7wEjdTpR1XMQ4rl42FK9SIXzZ4o9x7s6M4=
github.com/go-git/go-git/v5 v5.19.3/go.mod h1:Ye4C8dVigkqrv9UsB4NMdSLV4yAIkLiIhW61gcOyhl4=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@@ -586,8 +585,6 @@ github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdY
github.com/klauspost/compress v1.14.2/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE=
github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -753,14 +750,13 @@ github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuh
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pjbgf/sha1cd v0.6.0 h1:3WJ8Wz8gvDz29quX1OcEmkAlUg9diU4GxJHqs0/XiwU=
github.com/pjbgf/sha1cd v0.6.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=
github.com/pjbgf/sha1cd v0.7.0 h1:ZRNPKHj+gfkLBf0KJv/p1Hmz+7bqCT5o311YX0nq+DA=
github.com/pjbgf/sha1cd v0.7.0/go.mod h1:pKR5Li+qTCo+ebqITZfwPlJGoCFCvSO00qxjbNtTUFQ=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
@@ -857,8 +853,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE=
github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg=
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
@@ -946,8 +942,9 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw=
go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg=
golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -966,8 +963,8 @@ golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWP
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
golang.org/x/crypto v0.56.0 h1:GUh5Ii4J5jtcseSMiRqr1jXCNHoxjeV9Fmekc2oLy6Y=
golang.org/x/crypto v0.56.0/go.mod h1:OMW5y6CY9l38uPLmxU6l6pwcXp1obtLo3e6gT7gQR2I=
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=
@@ -1043,8 +1040,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1139,13 +1136,13 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/sys v0.49.0 h1:XbzkgYJHdqh/8m2Uu0W/dQv8nktxx4BFHp1M0gROTXA=
golang.org/x/sys v0.49.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0=
golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w=
golang.org/x/term v0.47.0 h1:cpyOwQkr2Nzxe2p6KnrTG7Ei2wtzjsuSjhpXXepZ/BM=
golang.org/x/term v0.47.0/go.mod h1:efAfAuG6RKABvg1ArGebRkvX6LXul14SM67o2e4mUCY=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1155,8 +1152,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8=
golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
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=
-2
View File
@@ -52,7 +52,6 @@ func Clone(dir, url string) error {
URL: url,
Tags: git.AllTags,
ReferenceName: plumbing.ReferenceName("refs/heads/main"),
SingleBranch: true,
})
if err != nil && gitCloneIgnoreErr(err) {
@@ -71,7 +70,6 @@ func Clone(dir, url string) error {
URL: url,
Tags: git.AllTags,
ReferenceName: plumbing.ReferenceName("refs/heads/master"),
SingleBranch: true,
})
if err != nil && gitCloneIgnoreErr(err) {
+36 -36
View File
@@ -7,7 +7,7 @@
msgid ""
msgstr "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2026-06-14 17:56+0200\n"
"POT-Creation-Date: 2026-08-31 19:21+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -189,7 +189,7 @@ msgstr ""
msgid "%d volumes removed successfully"
msgstr ""
#: ./pkg/recipe/git.go:197
#: ./pkg/recipe/git.go:198
#, c-format
msgid "%s (%s) has locally unstaged changes?"
msgstr ""
@@ -419,7 +419,7 @@ msgstr ""
msgid "%s sanitised as %s for new app"
msgstr ""
#: ./pkg/recipe/git.go:445
#: ./pkg/recipe/git.go:451
#, c-format
msgid "%s service is missing image tag?"
msgstr ""
@@ -902,7 +902,7 @@ msgstr ""
msgid "Generate the recipe catalogue"
msgstr ""
#: ./pkg/recipe/git.go:459
#: ./pkg/recipe/git.go:465
#, c-format
msgid "GetRecipeVersions encountered error for %s: %s (collected %d versions)"
msgstr ""
@@ -1815,7 +1815,7 @@ msgstr ""
msgid "are you sure?"
msgstr ""
#: ./pkg/recipe/git.go:162
#: ./pkg/recipe/git.go:163
#, c-format
msgid "attempting to checkout '%s' as chaos commit"
msgstr ""
@@ -1951,7 +1951,7 @@ msgstr ""
msgid "cancelled"
msgstr ""
#: ./pkg/catalogue/catalogue.go:59 ./pkg/recipe/git.go:251
#: ./pkg/catalogue/catalogue.go:59 ./pkg/recipe/git.go:252
#, c-format
msgid "cannot ensure %s is up-to-date, no git remotes configured"
msgstr ""
@@ -1966,7 +1966,7 @@ msgstr ""
msgid "cannot get label %s for %s"
msgstr ""
#: ./pkg/recipe/git.go:58
#: ./pkg/recipe/git.go:59
#, c-format
msgid "cannot redeploy previous chaos version (%s), did you mean to use \"--chaos\"?"
msgstr ""
@@ -2118,7 +2118,7 @@ msgstr ""
msgid "cmd"
msgstr ""
#: ./pkg/recipe/git.go:470
#: ./pkg/recipe/git.go:476
#, c-format
msgid "collected %s for %s"
msgstr ""
@@ -2456,7 +2456,7 @@ msgstr ""
msgid "destination directory does not exist"
msgstr ""
#: ./pkg/recipe/git.go:373
#: ./pkg/recipe/git.go:379
#, c-format
msgid "detected %s as tags for recipe %s"
msgstr ""
@@ -2653,7 +2653,7 @@ msgstr ""
msgid "ensure recipe: %s"
msgstr ""
#: ./pkg/recipe/git.go:56
#: ./pkg/recipe/git.go:57
#, c-format
msgid "ensuring env version %s"
msgstr ""
@@ -2751,12 +2751,12 @@ msgstr ""
msgid "failed to check git status of %s: %s"
msgstr ""
#: ./pkg/git/branch.go:95 ./pkg/recipe/git.go:231
#: ./pkg/git/branch.go:95 ./pkg/recipe/git.go:232
#, c-format
msgid "failed to check out %s in %s"
msgstr ""
#: ./pkg/recipe/git.go:412
#: ./pkg/recipe/git.go:418
#, c-format
msgid "failed to check out %s in %s: %s"
msgstr ""
@@ -2806,7 +2806,7 @@ msgstr ""
msgid "failed to generate random bytes: %w"
msgstr ""
#: ./pkg/recipe/git.go:421
#: ./pkg/recipe/git.go:427
#, c-format
msgid "failed to get compose config for %s: %s"
msgstr ""
@@ -2844,7 +2844,7 @@ msgstr ""
msgid "failed to parse image %s, saw: %s"
msgstr ""
#: ./pkg/recipe/git.go:431
#: ./pkg/recipe/git.go:437
#, c-format
msgid "failed to parse image for %s in %s: %s"
msgstr ""
@@ -2952,7 +2952,7 @@ msgstr ""
msgid "fetch all recipes"
msgstr ""
#: ./pkg/catalogue/catalogue.go:84 ./pkg/recipe/git.go:284
#: ./pkg/catalogue/catalogue.go:84 ./pkg/recipe/git.go:290
#, c-format
msgid "fetched latest git changes for %s"
msgstr ""
@@ -3070,12 +3070,12 @@ msgstr ""
msgid "git changes pushed"
msgstr ""
#: ./pkg/recipe/git.go:417
#: ./pkg/recipe/git.go:423
#, c-format
msgid "git checkout: %s in %s"
msgstr ""
#: ./pkg/git/clone.go:64 ./pkg/git/clone.go:102
#: ./pkg/git/clone.go:63 ./pkg/git/clone.go:100
#, c-format
msgid "git clone %s: cancelled due to interrupt"
msgstr ""
@@ -3085,17 +3085,17 @@ msgstr ""
msgid "git clone: %s"
msgstr ""
#: ./pkg/git/clone.go:89
#: ./pkg/git/clone.go:87
#, c-format
msgid "git clone: %s already exists"
msgstr ""
#: ./pkg/git/clone.go:59 ./pkg/git/clone.go:78 ./pkg/git/clone.go:87
#: ./pkg/git/clone.go:58 ./pkg/git/clone.go:76 ./pkg/git/clone.go:85
#, c-format
msgid "git clone: %s cloned successfully"
msgstr ""
#: ./pkg/git/clone.go:68
#: ./pkg/git/clone.go:67
msgid "git clone: main branch failed, attempting master branch"
msgstr ""
@@ -3150,7 +3150,7 @@ msgstr ""
msgid "git.coopcloud.tech repo exists"
msgstr ""
#: ./pkg/recipe/git.go:384
#: ./pkg/recipe/git.go:390
#, c-format
msgid "git: opening repository in %s"
msgstr ""
@@ -4061,7 +4061,7 @@ msgstr ""
msgid "proceed?"
msgstr ""
#: ./pkg/recipe/git.go:404
#: ./pkg/recipe/git.go:410
#, c-format
msgid "processing %s for %s"
msgstr ""
@@ -4129,7 +4129,7 @@ msgstr ""
msgid "re"
msgstr ""
#: ./pkg/recipe/git.go:157
#: ./pkg/recipe/git.go:158
#, c-format
msgid "read %s as tags for recipe %s"
msgstr ""
@@ -4865,17 +4865,17 @@ msgstr ""
msgid "skipping secret (because it already exists) on %s: %s"
msgstr ""
#: ./pkg/recipe/git.go:413
#: ./pkg/recipe/git.go:419
#, c-format
msgid "skipping tag %s: checkout failed: %s"
msgstr ""
#: ./pkg/recipe/git.go:422
#: ./pkg/recipe/git.go:428
#, c-format
msgid "skipping tag %s: invalid compose config: %s"
msgstr ""
#: ./pkg/recipe/git.go:432
#: ./pkg/recipe/git.go:438
#, c-format
msgid "skipping tag %s: invalid image reference in service %s: %s"
msgstr ""
@@ -4978,7 +4978,7 @@ msgstr ""
msgid "succeeded"
msgstr ""
#: ./pkg/recipe/git.go:184
#: ./pkg/recipe/git.go:185
#, c-format
msgid "successfully checked %s out to %s in %s"
msgstr ""
@@ -5138,17 +5138,17 @@ msgstr ""
msgid "un"
msgstr ""
#: ./pkg/recipe/git.go:193
#: ./pkg/recipe/git.go:194
#, c-format
msgid "unable to check git clean status in %s: %s"
msgstr ""
#: ./pkg/recipe/git.go:262
#: ./pkg/recipe/git.go:263
#, c-format
msgid "unable to check out default branch in %s: %s"
msgstr ""
#: ./pkg/git/clone.go:100
#: ./pkg/git/clone.go:98
#, c-format
msgid "unable to clean up git clone of %s: %s"
msgstr ""
@@ -5222,7 +5222,7 @@ msgstr ""
msgid "unable to discover SSH remote for %s"
msgstr ""
#: ./pkg/recipe/git.go:268
#: ./pkg/recipe/git.go:274
#, c-format
msgid "unable to fetch tags in %s: %s"
msgstr ""
@@ -5232,7 +5232,7 @@ msgstr ""
msgid "unable to get container matching %s: %s"
msgstr ""
#: ./pkg/recipe/git.go:280
#: ./pkg/recipe/git.go:286
#, c-format
msgid "unable to git pull in %s: %s"
msgstr ""
@@ -5261,12 +5261,12 @@ msgstr ""
msgid "unable to look up server context for %s: %s"
msgstr ""
#: ./cli/recipe/fetch.go:77 ./pkg/git/read.go:26 ./pkg/lint/recipe.go:491 ./pkg/recipe/git.go:242
#: ./cli/recipe/fetch.go:77 ./pkg/git/read.go:26 ./pkg/lint/recipe.go:491 ./pkg/recipe/git.go:243
#, c-format
msgid "unable to open %s: %s"
msgstr ""
#: ./pkg/recipe/git.go:257
#: ./pkg/recipe/git.go:258
#, c-format
msgid "unable to open git work tree in %s: %s"
msgstr ""
@@ -5326,7 +5326,7 @@ msgstr ""
msgid "unable to read new env %s: %s"
msgstr ""
#: ./pkg/recipe/git.go:247
#: ./pkg/recipe/git.go:248
#, c-format
msgid "unable to read remotes in %s: %s"
msgstr ""
@@ -5361,7 +5361,7 @@ msgstr ""
msgid "unable to reset commit after failed release attempt: %s"
msgstr ""
#: ./pkg/recipe/git.go:166
#: ./pkg/recipe/git.go:167
#, c-format
msgid "unable to resolve '%s': %s"
msgstr ""
Binary file not shown.
+143 -81
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2026-06-14 17:56+0200\n"
"PO-Revision-Date: 2026-02-28 13:52+0000\n"
"POT-Creation-Date: 2026-08-31 19:21+0000\n"
"PO-Revision-Date: 2026-09-01 22:35+0000\n"
"Last-Translator: chasqui <chasqui@cryptolab.net>\n"
"Language-Team: Spanish <https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/>\n"
"Language: es\n"
@@ -113,17 +113,25 @@ msgid ""
" # list apps of all servers which match a specific recipe\n"
" abra app ls -r gitea"
msgstr ""
" # Listar las aplicaciones instaladas\n"
" abra aplicacion listar\n"
" OPCIONES Y FLAGS\n"
" ────────────────\n"
" ejemplos de uso:\n"
"\n"
" # Listar las aplicaciones instaladas en un servidor específico\n"
" abra aplicacion listar -s 1312.net\n"
"┌─ Información detallada ─────────────────────────────────────┐\n"
"│ $ abra aplicacion listar --estado │\n"
"└─────────────────────────────────────────────────────────────┘\n"
"\n"
" # Listar aplicaciones instaladas en servidor específico con detalles\n"
" abra aplicacion listar -s 1312.net -S\n"
"┌─ Aplicaciones de una receta ────────────────────────────────┐\n"
"│ $ abra aplicacion listar --receta wordpress │\n"
"└─────────────────────────────────────────────────────────────┘\n"
"\n"
" # Listar en qué servidores está desplegada la aplicación \"gitea\"\n"
" abra aplicacion listar -r gitea"
"┌─ Receta + información detallada ────────────────────────────┐\n"
"│ $ abra aplicacion listar --receta wordpress --estado │\n"
"└─────────────────────────────────────────────────────────────┘\n"
"\n"
"┌─ Forma abreviada ───────────────────────────────────────────┐\n"
"│ $ abra app ls -S │\n"
"└─────────────────────────────────────────────────────────────┘"
#: cli/app/move.go:59
msgid ""
@@ -236,7 +244,21 @@ msgstr " abra aplicacion entorno listar \"nombre_app.dominio\""
#: cli/app/remove.go:45
msgid " abra app remove 1312.net"
msgstr " abra aplicacion borrar \"nombre_app.dominio\""
msgstr ""
" EJEMPLOS DE USO\n"
" ────────────────\n"
"\n"
"┌─ Borrar una aplicación ─────────────────────────────────────┐\n"
"│ $ abra aplicacion borrar \"nombre_app.dominio\" │\n"
"└─────────────────────────────────────────────────────────────┘\n"
"\n"
"┌─ Borrar sin confirmación ───────────────────────────────────┐\n"
"│ $ abra aplicacion borrar \"nombre_app.dominio\" --forzar │\n"
"└─────────────────────────────────────────────────────────────┘\n"
"\n"
"┌─ Borrar sin confirmación usando comando abreviado ──────────┐\n"
"│ $ abra app rm \"nombre_app.dominio\" -f │\n"
"└─────────────────────────────────────────────────────────────┘"
#: cli/app/secret.go:346
msgid " abra app secret rm 1312.net oauth_key"
@@ -292,7 +314,7 @@ msgstr "%d volumen(es) eliminados con éxito"
msgid "%d volumes removed successfully"
msgstr "%d volúmenes eliminados correctamente"
#: pkg/recipe/git.go:197
#: pkg/recipe/git.go:198
#, c-format
msgid "%s (%s) has locally unstaged changes?"
msgstr "%s (%s) ¿tiene cambios locales?"
@@ -513,7 +535,7 @@ msgstr "%s eliminado del almacén de contraseñas"
#: cli/app/new.go:224
#, c-format
msgid "%s requires secret generation before deploy, run \"abra app secret generate %s --all\""
msgstr "%s requiere generación de secretos antes del despliegue, ejecuta \"abra aplicacion secreto generar %s --all\""
msgstr "%s requiere generación de secretos antes del despliegue, ejecuta \"abra aplicacion secreto generar %s --todos\""
#: cli/app/new.go:228
#, c-format
@@ -525,7 +547,7 @@ msgstr "%s requiere inserción de secretos antes del despliegue (#generate=false
msgid "%s sanitised as %s for new app"
msgstr "%s sanitisado como %s para nueva aplicación"
#: pkg/recipe/git.go:445
#: pkg/recipe/git.go:451
#, c-format
msgid "%s service is missing image tag?"
msgstr "¿falta la etiqueta de imagen del servicio %s?"
@@ -1108,9 +1130,25 @@ msgid ""
"\n"
"Use \"--status/-S\" flag to query all servers for the live deployment status."
msgstr ""
"Genera un informe de todas las aplicaciones gestionadas.\n"
"┌──────────────────────────────────────────────────────────────┐\n"
"│ MANUAL DE USO · COMANDO «LISTAR» │\n"
"└──────────────────────────────────────────────────────────────┘\n"
" DESCRIPCIÓN\n"
" ───────────\n"
" El comando:\n"
"\n"
"Usa la opción \"--estado/-S\" para consultar en todos los servidores sobre el estado de despliegue."
" abra aplicacion listar\n"
"\n"
"Permite consultar rápidamente qué aplicaciones están siendo\n"
"gestionadas por Abra.\n"
"\n"
"Al ejecutarlo, Abra genera un informe con la lista de\n"
"aplicaciones disponibles y la información básica de cada una.\n"
"\n"
"En pocas palabras, puedes imaginar `listar` como una forma\n"
"de decirle a Abra:\n"
"\n"
" «muéstrame qué aplicaciones tengo»"
#: cli/app/new.go:321
msgid "Generate app secrets?"
@@ -1136,7 +1174,7 @@ msgstr "Genera 🥷 secretos (contraseñas) automáticamente"
msgid "Generate the recipe catalogue"
msgstr "📋 Muestra el catálogo de 📜 recetas"
#: pkg/recipe/git.go:459
#: pkg/recipe/git.go:465
#, c-format
msgid "GetRecipeVersions encountered error for %s: %s (collected %d versions)"
msgstr "GetRecipeVersions encontró un error para %s: %s (recopiladas %d versiones)"
@@ -1478,24 +1516,48 @@ msgid ""
"To delete everything without prompt, use the \"--force/-f\" or the \"--no-input/n\"\n"
"flag."
msgstr ""
"Elimina todo lo relacionado con una aplicación que ya está plegada.\n"
"┌──────────────────────────────────────────────────────────────┐\n"
"│ MANUAL DE USO · COMANDO «BORRAR» │\n"
"└──────────────────────────────────────────────────────────────┘\n"
" DESCRIPCIÓN\n"
" ───────────\n"
" El comando:\n"
"\n"
"Por defecto, pedirá confirmación antes de continuar. Se borrarán \n"
"todos los secretos, volúmenes y el archivo .env local de la aplicación.\n"
" abra aplicacion borrar \"nombre_app.dominio\"\n"
"\n"
"Ejecuta este comando solo si estás seguro de querer quitar por \n"
"completo la aplicación y todos sus datos asociados. Esta acción \n"
"es destructiva. ¡Ten cuidado!\n"
"elimina todo lo relacionado con una aplicación que ya está\n"
"desplegada.\n"
"\n"
"Si quieres borrar volúmenes o secretos específicos, \n"
"usa los subcomandos de eliminación bajo \"aplicación volumen\" \n"
"y \"aplicación secreto\" en su lugar.\n"
"Por defecto, Abra pedirá confirmación antes de continuar.\n"
"\n"
"Ten en cuenta que si borras el archivo .env local sin eliminar primero \n"
"volúmenes y secretos, Abra *no* podrá ayudarte a eliminarlos después.\n"
"La eliminación incluye:\n"
"\n"
"Para borrar todo sin confirmación, usa la opción \"--forzar/-f\" \n"
"o la opción \"--no-input/-n\"."
" ├── secretos\n"
" ├── volúmenes\n"
" └── archivo .env local\n"
"\n"
"\n"
" ¡ATENCIÓN!\n"
" ──────────\n"
"Esta es una acción destructiva.\n"
"\n"
"La aplicación y todos los datos asociados serán eliminados.\n"
"Ejecuta este comando únicamente si estás seguro de querer\n"
"quitar la aplicación por completo.\n"
"\n"
"\n"
"Si quieres eliminar volúmenes o secretos específicos, utiliza\n"
"los subcomandos correspondientes:\n"
"\n"
" abra aplicacion volumen\n"
" abra aplicacion secreto\n"
"\n"
"\n"
" IMPORTANTE\n"
" ──────────\n"
"Si eliminas el archivo .env local sin eliminar primero los\n"
"volúmenes y secretos, Abra no podrá ayudarte a eliminarlos\n"
"después."
#. translators: Short description for `app volume remove` command
#: cli/app/volume.go:90
@@ -2025,32 +2087,32 @@ msgid ""
"\n"
"Use \"{{.CommandPath}} [command] --help\" for more information about a command.{{end}}\n"
msgstr ""
"Uso:{{if .Runnable}}\n"
" {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}\n"
" {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}\n"
"\n"
"Comando:\n"
" {{.NameAndAliases}}{{end}}{{if .HasExample}}\n"
"\n"
"Ejemplos:\n"
" # Nota: \"1312.net\" es solo un ejemplo de nombre de aplicación.\n"
"# Reemplázalo por el dominio o nombre real de tu aplicación.\n"
"COMANDO ABREVIADO\n"
"─────────────────\n"
" {{range .Aliases}}{{.}}{{end}}\n"
"\n"
"{{if .HasExample}}\n"
"{{.Example}}{{end}}{{if .HasAvailableSubCommands}}\n"
"\n"
"Comandos disponibles:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name \"help\"))}}\n"
" {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}\n"
"\n"
"Opciones:\n"
"{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}\n"
"\n"
"Opciones globales:\n"
"{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}\n"
" {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasHelpSubCommands}}\n"
"\n"
"Temas de ayuda adicionales:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}\n"
" {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}\n"
" {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}\n"
"\n"
"Use \"{{.CommandPath}} [command] --help\" para más información sobre un comando.{{end}}\n"
" SINTAXIS\n"
" ────────{{if .Runnable}}\n"
" {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}\n"
" {{.CommandPath}} [command]{{end}}\n"
"\n"
"Opciones:{{if .HasAvailableLocalFlags}}\n"
"{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}\n"
"\n"
"Opciones globales:{{if .HasAvailableInheritedFlags}}\n"
"{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}\n"
"\n"
"{{if .HasAvailableSubCommands}}\n"
"Use \"{{.CommandPath}} [comando] --help\" para más información sobre un comando.{{end}}\n"
#: cli/recipe/fetch.go:28
msgid "Using \"--force/-f\" Git syncs an existing recipe. It does not erase unstaged changes."
@@ -2348,7 +2410,7 @@ msgstr "¿A la aplicación le falta la etiqueta de imagen?"
msgid "are you sure?"
msgstr "¿Estás segurx?"
#: pkg/recipe/git.go:162
#: pkg/recipe/git.go:163
#, c-format
msgid "attempting to checkout '%s' as chaos commit"
msgstr "Intentando extraer '%s' como el commit de caos"
@@ -2491,7 +2553,7 @@ msgstr "no se pueden leer las recetas locales: %s"
msgid "cancelled"
msgstr "cancelado"
#: pkg/catalogue/catalogue.go:59 pkg/recipe/git.go:251
#: pkg/catalogue/catalogue.go:59 pkg/recipe/git.go:252
#, c-format
msgid "cannot ensure %s is up-to-date, no git remotes configured"
msgstr "no se puede asegurar que %s esté actualizado, no hay remotos de git configurados"
@@ -2506,7 +2568,7 @@ msgstr "no se puede encontrar la aplicación con el nombre %s"
msgid "cannot get label %s for %s"
msgstr "no se puede obtener la etiqueta %s para %s"
#: pkg/recipe/git.go:58
#: pkg/recipe/git.go:59
#, c-format
msgid "cannot redeploy previous chaos version (%s), did you mean to use \"--chaos\"?"
msgstr "no se puede redeplegar la versión anterior de caos (%s), ¿Era tu intención usar \"--caos\"?"
@@ -2666,7 +2728,7 @@ msgstr "tarea: Actualizar tags de una imagen"
msgid "cmd"
msgstr ""
#: pkg/recipe/git.go:470
#: pkg/recipe/git.go:476
#, c-format
msgid "collected %s for %s"
msgstr "colectado %s para %s"
@@ -3005,7 +3067,7 @@ msgstr "tiempo de espera agotado 🟠"
msgid "destination directory does not exist"
msgstr "el directorio de destino no existe"
#: pkg/recipe/git.go:373
#: pkg/recipe/git.go:379
#, c-format
msgid "detected %s as tags for recipe %s"
msgstr "detectado(s) %s como etiquetas para la receta %s"
@@ -3202,7 +3264,7 @@ msgstr "asegúrate que \"image: ...\" esté establecido en todos los servicios"
msgid "ensure recipe: %s"
msgstr "asegura receta: %s"
#: pkg/recipe/git.go:56
#: pkg/recipe/git.go:57
#, c-format
msgid "ensuring env version %s"
msgstr "asegurando la versión del entorno (env) %s"
@@ -3304,12 +3366,12 @@ msgstr "🛑 error al agregar notas de la versión: %s"
msgid "failed to check git status of %s: %s"
msgstr "🛑 error al comprobar el estado de git de %s: %s"
#: pkg/git/branch.go:95 pkg/recipe/git.go:231
#: pkg/git/branch.go:95 pkg/recipe/git.go:232
#, c-format
msgid "failed to check out %s in %s"
msgstr "🛑 error al cambiar a %s en %s"
#: pkg/recipe/git.go:412
#: pkg/recipe/git.go:418
#, c-format
msgid "failed to check out %s in %s: %s"
msgstr "🛑 error al cambiar a %s en %s: %s"
@@ -3359,7 +3421,7 @@ msgstr "🛑 error al crear el volumen %s en %s: %s"
msgid "failed to generate random bytes: %w"
msgstr "🛑 error al generar bytes aleatorios: %w"
#: pkg/recipe/git.go:421
#: pkg/recipe/git.go:427
#, c-format
msgid "failed to get compose config for %s: %s"
msgstr "🛑 error al obtener la configuración para %s: %s"
@@ -3397,7 +3459,7 @@ msgstr "🛑 error al migrar la configuración de la aplicación: %s"
msgid "failed to parse image %s, saw: %s"
msgstr "🛑 error al analizar la imagen %s; detectado: %s"
#: pkg/recipe/git.go:431
#: pkg/recipe/git.go:437
#, c-format
msgid "failed to parse image for %s in %s: %s"
msgstr "🛑 error al analizar la imagen %s; en: %s: %s"
@@ -3505,7 +3567,7 @@ msgstr "obtener [receta | --todos] [opciones]"
msgid "fetch all recipes"
msgstr "Obtener todas las recetas"
#: pkg/catalogue/catalogue.go:84 pkg/recipe/git.go:284
#: pkg/catalogue/catalogue.go:84 pkg/recipe/git.go:290
#, c-format
msgid "fetched latest git changes for %s"
msgstr "Se obtuvieron los últimos cambios de Git para %s"
@@ -3625,12 +3687,12 @@ msgstr "cambios de git confirmados"
msgid "git changes pushed"
msgstr "cambios de git enviados (push)"
#: pkg/recipe/git.go:417
#: pkg/recipe/git.go:423
#, c-format
msgid "git checkout: %s in %s"
msgstr ""
#: pkg/git/clone.go:64 pkg/git/clone.go:102
#: pkg/git/clone.go:63 pkg/git/clone.go:100
#, c-format
msgid "git clone %s: cancelled due to interrupt"
msgstr "git clone %s: cancelado por interrupción"
@@ -3640,17 +3702,17 @@ msgstr "git clone %s: cancelado por interrupción"
msgid "git clone: %s"
msgstr ""
#: pkg/git/clone.go:89
#: pkg/git/clone.go:87
#, c-format
msgid "git clone: %s already exists"
msgstr "git clone: %s ya existe"
#: pkg/git/clone.go:59 pkg/git/clone.go:78 pkg/git/clone.go:87
#: pkg/git/clone.go:58 pkg/git/clone.go:76 pkg/git/clone.go:85
#, c-format
msgid "git clone: %s cloned successfully"
msgstr "git clone: %s clonado correctamente"
#: pkg/git/clone.go:68
#: pkg/git/clone.go:67
msgid "git clone: main branch failed, attempting master branch"
msgstr "git clone: error en la rama principal; intentando la rama master"
@@ -3705,7 +3767,7 @@ msgstr "git-nombre"
msgid "git.coopcloud.tech repo exists"
msgstr "el repositorio git.coopcloud.tech existe"
#: pkg/recipe/git.go:384
#: pkg/recipe/git.go:390
#, c-format
msgid "git: opening repository in %s"
msgstr "git: abriendo repositorio en %s"
@@ -4643,7 +4705,7 @@ msgstr "mostrar salida legible por máquina"
msgid "proceed?"
msgstr "¿procedemos?"
#: pkg/recipe/git.go:404
#: pkg/recipe/git.go:410
#, c-format
msgid "processing %s for %s"
msgstr "procesando %s para %s"
@@ -4713,7 +4775,7 @@ msgstr ""
msgid "re"
msgstr ""
#: pkg/recipe/git.go:157
#: pkg/recipe/git.go:158
#, c-format
msgid "read %s as tags for recipe %s"
msgstr "leer %s como etiquetas para la receta %s"
@@ -5459,17 +5521,17 @@ msgstr "omitiendo generación de %s (generate=false)"
msgid "skipping secret (because it already exists) on %s: %s"
msgstr "omitiendo secreto (porque ya existe) en %s: %s"
#: pkg/recipe/git.go:413
#: pkg/recipe/git.go:419
#, c-format
msgid "skipping tag %s: checkout failed: %s"
msgstr "omitiendo la etiqueta %s: error en el checkout: %s"
#: pkg/recipe/git.go:422
#: pkg/recipe/git.go:428
#, c-format
msgid "skipping tag %s: invalid compose config: %s"
msgstr "omitiendo la etiqueta %s: configuración de compose inválida: %s"
#: pkg/recipe/git.go:432
#: pkg/recipe/git.go:438
#, c-format
msgid "skipping tag %s: invalid image reference in service %s: %s"
msgstr "omitiendo la etiqueta %s: referencia de imagen inválida en el servicio %s: %s"
@@ -5572,7 +5634,7 @@ msgstr "recortado %s a %s para el análisis"
msgid "succeeded"
msgstr "exitoso"
#: pkg/recipe/git.go:184
#: pkg/recipe/git.go:185
#, c-format
msgid "successfully checked %s out to %s in %s"
msgstr "se comprobó correctamente %s de %s en %s"
@@ -5734,17 +5796,17 @@ msgstr ""
msgid "un"
msgstr ""
#: pkg/recipe/git.go:193
#: pkg/recipe/git.go:194
#, c-format
msgid "unable to check git clean status in %s: %s"
msgstr "no se pudo comprobar el estado de git clean en %s: %s"
#: pkg/recipe/git.go:262
#: pkg/recipe/git.go:263
#, c-format
msgid "unable to check out default branch in %s: %s"
msgstr "no se pudo cambiar a la rama predeterminada en %s: %s"
#: pkg/git/clone.go:100
#: pkg/git/clone.go:98
#, c-format
msgid "unable to clean up git clone of %s: %s"
msgstr "no se pudo limpiar la clonación de Git de %s: %s"
@@ -5818,7 +5880,7 @@ msgstr "no se pudieron determinar las reglas de versionado de %s; listando todas
msgid "unable to discover SSH remote for %s"
msgstr "no se pudo detectar el remoto SSH para %s"
#: pkg/recipe/git.go:268
#: pkg/recipe/git.go:274
#, c-format
msgid "unable to fetch tags in %s: %s"
msgstr "no se pudieron obtener las etiquetas en %s: %s"
@@ -5828,7 +5890,7 @@ msgstr "no se pudieron obtener las etiquetas en %s: %s"
msgid "unable to get container matching %s: %s"
msgstr "no se pudo obtener el contenedor que coincide con %s: %s"
#: pkg/recipe/git.go:280
#: pkg/recipe/git.go:286
#, c-format
msgid "unable to git pull in %s: %s"
msgstr "no se pudo hacer git pull en %s: %s"
@@ -5858,12 +5920,12 @@ msgid "unable to look up server context for %s: %s"
msgstr "no se pudo obtener el contexto del servidor para %s: %s"
#: cli/recipe/fetch.go:77 pkg/git/read.go:26 pkg/lint/recipe.go:491
#: pkg/recipe/git.go:242
#: pkg/recipe/git.go:243
#, c-format
msgid "unable to open %s: %s"
msgstr "no se pudo abrir %s: %s"
#: pkg/recipe/git.go:257
#: pkg/recipe/git.go:258
#, c-format
msgid "unable to open git work tree in %s: %s"
msgstr "no se pudo abrir el git work tree en %s: %s"
@@ -5923,7 +5985,7 @@ msgstr "no se pudo consultar el estado de %s: %s"
msgid "unable to read new env %s: %s"
msgstr "no se pudo leer el nuevo env %s: %s"
#: pkg/recipe/git.go:247
#: pkg/recipe/git.go:248
#, c-format
msgid "unable to read remotes in %s: %s"
msgstr "no se pudieron leer los remotos en %s: %s"
@@ -5959,7 +6021,7 @@ msgstr "no se pudo renderizar a JSON: %s"
msgid "unable to reset commit after failed release attempt: %s"
msgstr "no se pudo establecer los streams de IO como terminal raw: %s"
#: pkg/recipe/git.go:166
#: pkg/recipe/git.go:167
#, c-format
msgid "unable to resolve '%s': %s"
msgstr "no se pudo resolver '%s': %s"
+7 -1
View File
@@ -16,6 +16,7 @@ import (
"coopcloud.tech/tagcmp"
"github.com/distribution/reference"
"github.com/go-git/go-git/v5"
gitCfg "github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
)
@@ -262,7 +263,12 @@ func (r Recipe) EnsureUpToDate() error {
return errors.New(i18n.G("unable to check out default branch in %s: %s", r.Dir, err))
}
fetchOpts := &git.FetchOptions{Tags: git.AllTags}
// the refspec is passed explicitly, because a repository cloned by an older abra has a
// single-branch refspec stored in its config and would otherwise never see other branches
fetchOpts := &git.FetchOptions{
Tags: git.AllTags,
RefSpecs: []gitCfg.RefSpec{"+refs/heads/*:refs/remotes/origin/*"},
}
if err := repo.Fetch(fetchOpts); err != nil {
if !strings.Contains(err.Error(), "already up-to-date") {
return errors.New(i18n.G("unable to fetch tags in %s: %s", r.Dir, err))
+1 -1
View File
@@ -3,7 +3,7 @@ version: "3.8"
services:
app:
image: nginx:1.31.3
image: nginx:1.31.6
secrets:
- test_pass_one
- test_pass_two
+33
View File
@@ -122,6 +122,39 @@ teardown(){
assert_failure
}
@test "create new app with commit from another branch" {
branchHash=$(_get_other_branch_hash)
if [[ -z "$branchHash" ]]; then
skip "$TEST_RECIPE has no branch besides main"
fi
# re-clone the recipe the way an older abra did, with a single-branch refspec. --no-local
# forces a real transfer, a local clone would hardlink the whole object database and leave
# the commit reachable
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE"
assert_success
run git clone -q --no-local --single-branch --branch main \
"$ABRA_DIR/origin-recipes/$TEST_RECIPE.git" "$ABRA_DIR/recipes/$TEST_RECIPE"
assert_success
# the commit has to be genuinely missing, otherwise this test passes for the wrong reason
run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" rev-parse --verify "$branchHash^{commit}"
assert_failure
run $ABRA app new "$TEST_RECIPE" "$branchHash" \
--no-input \
--server "$TEST_SERVER" \
--domain "$TEST_APP_DOMAIN"
assert_success
assert_exists "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
# the recipe names itself in TYPE, which differs per branch, so only the version is checked
run grep -q "TYPE=.*:${branchHash}$" \
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
assert_success
}
@test "does not overwrite existing env files" {
run $ABRA app new "$TEST_RECIPE" \
--no-input \
+7
View File
@@ -56,6 +56,13 @@ _get_tag_hash() {
echo $(git -C "$ABRA_DIR/recipes/$TEST_RECIPE" rev-list -n 1 "$1")
}
_get_other_branch_hash() {
# asked from the origin mirror, not from the recipe checkout, so that the result does not
# depend on what has been fetched. empty when the recipe only has a default branch
echo $(git ls-remote "$ABRA_DIR/origin-recipes/$TEST_RECIPE.git" \
| grep 'refs/heads/' | grep -v 'refs/heads/main$' | head -1 | cut -f1)
}
_get_head_hash() {
echo $(git -C "$ABRA_DIR/recipes/$TEST_RECIPE" show -s --format="%H" HEAD)
}
+1 -1
View File
@@ -3,7 +3,7 @@ version: "3.8"
services:
app:
image: nginx:1.31.3
image: nginx:1.31.6
networks:
- proxy
deploy:
+3 -3
View File
@@ -3,16 +3,16 @@ kind: pipeline
name: coopcloud.tech/tagcmp
steps:
- name: gofmt
image: golang:1.26
image: golang:1.27
commands:
- test -z "$(gofmt -l .)"
- name: go build
image: golang:1.26
image: golang:1.27
commands:
- go build -v .
- name: go test
image: golang:1.26
image: golang:1.27
commands:
- go test . -cover
+1
View File
@@ -5,3 +5,4 @@ profile.out
.tmp/
.git-dist/
.vscode
build/tools/
+4
View File
@@ -329,6 +329,10 @@ func (c *Config) unmarshalCore() {
if parsed := parseConfigBool(s.Options.Get(protectHFSKey)); parsed.IsSet() {
c.Core.ProtectHFS = parsed
}
if s.Options.Get(repositoryFormatVersionKey) == string(format.Version_1) {
c.Core.RepositoryFormatVersion = format.Version_1
}
}
func (c *Config) unmarshalUser() {
@@ -139,8 +139,12 @@ func (dw *deltaSelector) fixAndBreakChains(objectsToPack []*ObjectToPack) error
m[otp.Hash()] = otp
}
// visiting holds the objects on the current resolution path, so that a
// delta chain looping back on itself can be detected and broken.
visiting := make(map[plumbing.Hash]bool)
for _, otp := range objectsToPack {
if err := dw.fixAndBreakChainsOne(m, otp); err != nil {
if err := dw.fixAndBreakChainsOne(m, otp, visiting); err != nil {
return err
}
}
@@ -148,7 +152,11 @@ func (dw *deltaSelector) fixAndBreakChains(objectsToPack []*ObjectToPack) error
return nil
}
func (dw *deltaSelector) fixAndBreakChainsOne(objectsToPack map[plumbing.Hash]*ObjectToPack, otp *ObjectToPack) error {
func (dw *deltaSelector) fixAndBreakChainsOne(
objectsToPack map[plumbing.Hash]*ObjectToPack,
otp *ObjectToPack,
visiting map[plumbing.Hash]bool,
) error {
if !otp.Object.Type().IsDelta() {
return nil
}
@@ -174,7 +182,21 @@ func (dw *deltaSelector) fixAndBreakChainsOne(objectsToPack map[plumbing.Hash]*O
return dw.undeltify(otp)
}
if err := dw.fixAndBreakChainsOne(objectsToPack, base); err != nil {
// Mark this object as being resolved before looking at its base, so that
// a delta based on itself is caught by the check below.
h := otp.Hash()
visiting[h] = true
defer delete(visiting, h)
// A delta chain that loops back onto an object we are already resolving
// cannot be written: every delta needs its base written first. Break the
// chain here instead of following the cycle, which would recurse until
// the goroutine stack is exhausted.
if visiting[do.BaseHash()] {
return dw.undeltify(otp)
}
if err := dw.fixAndBreakChainsOne(objectsToPack, base, visiting); err != nil {
return err
}
+34 -2
View File
@@ -474,6 +474,14 @@ type TreeWalker struct {
recursive bool
seen map[plumbing.Hash]bool
// skipPathValidation disables the pathutil.ValidTreePath check in Next.
// It is set by inspection-only callers (e.g. the revlist object walk)
// that never funnel entry names into the filesystem and must enumerate
// trees faithfully, including entries with names upstream Git accepts
// but that are unsafe to materialise (control characters, `.git`-shaped
// names).
skipPathValidation bool
s storer.EncodedObjectStorer
t *Tree
}
@@ -496,10 +504,32 @@ func NewTreeWalker(t *Tree, recursive bool, seen map[plumbing.Hash]bool) *TreeWa
}
}
// SkipPathValidation disables the pathutil.ValidTreePath check performed by
// Next, and must be called before the first Next.
//
// It is for inspection-only walks that never funnel an entry name into the
// filesystem — enumerating which objects exist, rather than materialising
// them — and that must therefore see the tree faithfully, including entries
// whose names upstream Git accepts but that are unsafe to check out. Callers
// that hand the returned name to filesystem or archive output must not use
// it; path safety for those is enforced at the materialisation boundaries
// (FindEntry, TreeEntryFile, archive, FileIter).
func (w *TreeWalker) SkipPathValidation() {
w.skipPathValidation = true
}
// Next returns the next object from the tree. Objects are returned in order
// and subtrees are included. After the last object has been returned further
// calls to Next() will return io.EOF.
//
// Each entry's name is validated against pathutil.ValidTreePath as it
// surfaces, so callers that funnel the returned name into filesystem
// or archive output can trust it is free of `.git`-shaped components,
// HFS+/NTFS variants, Windows reserved names, and traversal sequences.
// A malformed entry stops the walk with the validator's error;
// inspection-only callers that need to enumerate raw, unvalidated
// names can read Tree.Entries directly or call SkipPathValidation.
//
// In the current implementation any objects which cannot be found in the
// underlying repository will be skipped automatically. It is possible that this
// may change in future versions.
@@ -536,8 +566,10 @@ func (w *TreeWalker) Next() (name string, entry TreeEntry, err error) {
continue
}
if err := pathutil.ValidTreePath(entry.Name); err != nil {
return name, entry, err
if !w.skipPathValidation {
if err := pathutil.ValidTreePath(entry.Name); err != nil {
return name, entry, err
}
}
if entry.Mode == filemode.Dir {
+6
View File
@@ -106,6 +106,12 @@ func transformChildren(t *Tree) ([]noder.Noder, error) {
ret := make([]noder.Noder, 0, len(t.Entries))
walker := NewTreeWalker(t, false, nil) // don't recurse
// The diff walk is read-only and never materialises entry names into the
// filesystem, so it must enumerate the tree faithfully — including entries
// with names that are unsafe to check out but valid per upstream Git (e.g.
// control characters). Path safety is enforced at materialisation
// boundaries (FindEntry, TreeEntryFile, archive, FileIter), not here.
walker.SkipPathValidation()
// don't defer walker.Close() for efficiency reasons.
for {
_, e, err = walker.Next()
+42
View File
@@ -110,6 +110,48 @@ func (r ReferenceName) IsTag() bool {
return strings.HasPrefix(string(r), refTagPrefix)
}
// IsSafe reports whether the reference name can be safely turned into a path
// under the .git directory, mirroring Git's refname_is_safe (refs.c). A name
// is safe when it is either:
//
// - under "refs/", non-empty after the prefix, containing no backslash and
// no empty, "." or ".." path component (so it cannot escape the refs/
// sub-tree, or alias another name, once turned into a path); or
// - a one-level pseudo-ref whose spelling is restricted to [A-Z_]
// (e.g. HEAD, ORIG_HEAD, FETCH_HEAD).
//
// Everything else — a lowercase or mixed one-level name such as "config" or
// "index", an absolute or drive-prefixed name, or a refs/ name that escapes —
// is unsafe, because it could resolve onto unrelated repository metadata.
func (r ReferenceName) IsSafe() bool {
s := string(r)
if s == "" {
return false
}
if rest, ok := strings.CutPrefix(s, refPrefix); ok {
// '\' is a path separator on Windows, so a refs/ name containing one
// could escape the sub-tree or alias another name once turned into a
// path; reject it outright (check_refname_format forbids '\' too).
if rest == "" || strings.Contains(rest, "\\") {
return false
}
for part := range strings.SplitSeq(rest, "/") {
if part == "" || part == "." || part == ".." {
return false
}
}
return true
}
for i := 0; i < len(s); i++ {
if (s[i] < 'A' || s[i] > 'Z') && s[i] != '_' {
return false
}
}
return true
}
func (r ReferenceName) String() string {
return string(r)
}
+7
View File
@@ -187,6 +187,13 @@ func iterateCommitTrees(
cb(tree.Hash)
treeWalker := object.NewTreeWalker(tree, true, seen)
// This walk only enumerates which objects are reachable, to decide what
// to send; it never materialises an entry name into the filesystem. It
// must therefore enumerate the tree faithfully, including entries with
// names that are unsafe to check out but valid per upstream Git (e.g.
// control characters). Path safety is enforced at materialisation
// boundaries (FindEntry, TreeEntryFile, archive, FileIter), not here.
treeWalker.SkipPathValidation()
for {
_, e, err := treeWalker.Next()
+379 -22
View File
@@ -6,8 +6,12 @@ import (
"context"
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"io"
"net"
"net/http"
"net/netip"
"net/url"
"reflect"
"strconv"
@@ -73,7 +77,7 @@ func advertisedReferences(ctx context.Context, s *session, serviceName string) (
s.endpoint.String(), infoRefsPath, serviceName,
)
req, err := http.NewRequest(http.MethodGet, url, nil)
req, err := newRequest(http.MethodGet, url, nil)
if err != nil {
return nil, err
}
@@ -177,6 +181,24 @@ func NewClient(c *http.Client) transport.Transport {
// and other custom options specific to the client.
// If the net/http client is nil or empty, it will use a net/http client configured
// with http.DefaultTransport.
//
// Credentials this client adds where the transport cannot see them are not
// subject to the redirect stripping described on AuthMethod: a RoundTripper
// injects after the hop is decided, and Client.Jar is consulted after
// CheckRedirect, so a domain cookie still follows a redirect to a subdomain
// the transport counts as another origin. Apply them in an AuthMethod instead
// if that is not wanted. A CheckRedirect hook set on this client runs
// alongside the transport's own, but any header it adds when a redirect
// leaves the repository's origin is discarded the same way.
//
// A RoundTripper is therefore also how to authenticate to a new origin a
// redirect has moved the repository to: match on the request URL and inject
// the credential only for that origin, so it is not sent anywhere else. The
// transport keeps its own CheckRedirect on the copy it makes of this client,
// so the policy and the origin checks still apply.
//
// None of this applies to a RoundTripper that follows redirects itself:
// CheckRedirect is not consulted then, so no stripping happens at all.
func NewClientWithOptions(c *http.Client, opts *ClientOptions) transport.Transport {
if c == nil {
c = &http.Client{
@@ -370,11 +392,20 @@ func (s *session) ModifyEndpointIfRedirect(res *http.Response) error {
if !strings.HasSuffix(r.URL.Path, infoRefsPath) {
return fmt.Errorf("http redirect: target %q does not end with %s", r.URL.Path, infoRefsPath)
}
if r.URL.Scheme != "http" && r.URL.Scheme != "https" {
// A scheme is case-insensitive per RFC 3986, and checkRedirect folds case
// when it reads the same hop, so fold here too rather than reject a
// spelling that check let through. url.Parse and transport.NewEndpoint
// both lowercase what they parse, so only a hand-built URL or Endpoint
// arrives uppercased. The folded form is what gets stored below, so every
// later request built from the endpoint carries the canonical spelling.
scheme := strings.ToLower(r.URL.Scheme)
if scheme != "http" && scheme != "https" {
return fmt.Errorf("http redirect: unsupported scheme %q", r.URL.Scheme)
}
if r.URL.Scheme != s.endpoint.Protocol &&
!(s.endpoint.Protocol == "http" && r.URL.Scheme == "https") {
// schemeUpgrade rather than an inline comparison, so the one cross-scheme
// change go-git permits has a single definition shared with
// credentialsMayFollow.
if !strings.EqualFold(scheme, s.endpoint.Protocol) && !schemeUpgrade(s.endpoint.Protocol, scheme) {
return fmt.Errorf("http redirect: changes scheme from %q to %q", s.endpoint.Protocol, r.URL.Scheme)
}
@@ -384,7 +415,20 @@ func (s *session) ModifyEndpointIfRedirect(res *http.Response) error {
return err
}
if host != s.endpoint.Host || effectivePort(r.URL.Scheme, port) != effectivePort(s.endpoint.Protocol, s.endpoint.Port) {
// The session stores the endpoint and re-applies its credentials on every
// later request, so clear them once the redirect has left the origin they
// were issued for. This uses the same predicate as stripCredentials, so
// both halves share one definition of an origin.
//
// The two are deliberately asymmetric in one respect: stripCredentials is
// sticky over the whole chain, so an origin -> evil -> origin redirect
// leaves the discovery GET's later hops unauthenticated even though the
// chain returned home. This compares the endpoint only against the final
// URL, so the same round trip leaves the session authenticated. That is
// not a leak - the final URL's origin is the original one - but it means
// such a chain can make the discovery GET anonymous while the session's
// POSTs are authenticated, which can surface as a confusing 401.
if !credentialsMayFollow(endpointURL(s.endpoint), r.URL) {
s.endpoint.User = ""
s.endpoint.Password = ""
s.auth = nil
@@ -393,7 +437,7 @@ func (s *session) ModifyEndpointIfRedirect(res *http.Response) error {
s.endpoint.Host = host
s.endpoint.Port = port
s.endpoint.Protocol = r.URL.Scheme
s.endpoint.Protocol = scheme
s.endpoint.Path = r.URL.Path[:len(r.URL.Path)-len(infoRefsPath)]
return nil
}
@@ -419,19 +463,132 @@ func endpointPort(port string) (int, error) {
return parsed, nil
}
func effectivePort(scheme string, port int) int {
if port != 0 {
return port
}
// schemeUpgrade reports whether the scheme transition from one URL to another
// is the one cross-scheme change go-git permits: a plain-http origin upgrading
// to https. It strictly improves confidentiality and is how servers steer
// clients off cleartext.
//
// Permitting it at all is a deliberate deviation: curl, git and the Fetch
// standard all count scheme as part of host identity and drop credentials on
// the upgrade. Auth is sent pre-emptively here, so an http origin has already
// spent its credential in cleartext on the first request and refusing the
// upgrade would break the clone without unspending it. The host is unchanged,
// where an on-path attacker needs a valid certificate to receive anything.
func schemeUpgrade(from, to string) bool {
return strings.EqualFold(from, "http") && strings.EqualFold(to, "https")
}
switch strings.ToLower(scheme) {
case "http":
return 80
case "https":
return 443
default:
return 0
// canonicalHost returns u's hostname in the form origins are compared in.
//
// An address literal is normalised by netip, so the many spellings of one
// address are one origin. Two literals are the same origin exactly when netip
// parses them to the same Addr, which is also how the WHATWG URL Standard
// compares hosts. An IPv4-mapped literal is deliberately not unmapped onto
// the IPv4 it dials: reaching the same endpoint is not the same authority,
// since net/http sends the literal as written in Host and a server may route
// the two spellings to different virtual hosts.
//
// netip also keeps a scope zone verbatim, which is what origin comparison
// needs: net resolves a zone to an interface by exact name, so folding %eth0
// onto %ETH0 would call two hosts the same origin that net dials down
// different interfaces.
//
// A registered name is ASCII-lowercased. That fold is the only liberty taken;
// every other difference in spelling is a different origin.
//
// A trailing root dot is one such difference and is kept, for the same reason
// as the IPv4-mapped literal: curl and the WHATWG URL Standard both hold
// "example.com." and "example.com" to be distinct hosts, and although
// crypto/tls and crypto/x509 fold the dot when they authenticate the peer,
// net/http sends the name as written in Host.
//
// The fold is deliberately ASCII-only. strings.ToLower and strings.EqualFold
// apply Unicode case mapping, which folds U+03C2 onto U+03C3 and so would
// call two hosts the same origin when they resolve to different servers. An
// ASCII-only fold cannot merge two names DNS keeps apart.
//
// No IDNA mapping is applied either, so a unicode hostname is a different
// origin from the punycode encoding of it, and from another Unicode case of
// itself, even though all three reach the same server. Mapping through
// golang.org/x/net/idna would join them, but it can only widen this equality,
// never narrow it, so leaving it out can cost a credential across such a
// redirect and cannot forward one. Against that cost, go-git pins x/net while
// net/http uses the copy vendored into the toolchain: the two are versioned
// separately, so a release that moves the Unicode tables under one and not
// the other would have this merge origins net/http still dials apart. That is
// the failure this comparison exists to prevent, and comparing bytes has no
// such mode.
func canonicalHost(u *url.URL) string {
host := u.Hostname()
if addr, err := netip.ParseAddr(host); err == nil {
return addr.String()
}
b := []byte(host)
for i := range b {
if b[i] >= 'A' && b[i] <= 'Z' {
b[i] += 'a' - 'A'
}
}
return string(b)
}
// effectivePort returns u's port as the connection will use it: the scheme's
// well-known port when the URL does not spell one out, and without leading
// zeroes, so "https://x", "https://x:443" and "https://x:0443" all agree.
func effectivePort(u *url.URL) string {
port := u.Port()
if port == "" {
switch strings.ToLower(u.Scheme) {
case "http":
return "80"
case "https":
return "443"
default:
return ""
}
}
if trimmed := strings.TrimLeft(port, "0"); trimmed != "" {
return trimmed
}
return "0"
}
// credentialsMayFollow reports whether credentials issued for one URL may be
// sent to another.
//
// The relation is deliberately asymmetric: scheme, host and effective port
// must all match, except that a plain http origin may upgrade to https on the
// same host (see schemeUpgrade). That exception is confined to the two
// default ports: 80 to 443 is the upgrade servers actually steer clients
// through, whereas a non-default port carries no such convention, so
// http://host:8080 to https://host:8443 is a move to another origin like any
// other port change.
//
// Host matching is exact. Unlike Go's http.Client, which forwards credentials
// from a host to any subdomain of it, a subdomain is a different origin here —
// matching canonical git and libcurl.
func credentialsMayFollow(from, to *url.URL) bool {
if canonicalHost(from) != canonicalHost(to) {
return false
}
if strings.EqualFold(from.Scheme, to.Scheme) {
return effectivePort(from) == effectivePort(to)
}
return schemeUpgrade(from.Scheme, to.Scheme) &&
effectivePort(from) == "80" && effectivePort(to) == "443"
}
// endpointURL renders an Endpoint's origin as a URL, so that the session's
// credential clearing and the per-hop stripping share one definition of an
// origin and cannot drift apart.
func endpointURL(ep *transport.Endpoint) *url.URL {
host := strings.Trim(ep.Host, "[]")
if ep.Port != 0 {
host = net.JoinHostPort(host, strconv.Itoa(ep.Port))
} else if strings.Contains(host, ":") {
host = "[" + host + "]"
}
return &url.URL{Scheme: ep.Protocol, Host: host}
}
func (c *client) cloneHTTPClient(transport http.RoundTripper) *http.Client {
@@ -448,26 +605,215 @@ func wrapCheckRedirect(policy RedirectPolicy, next func(*http.Request, []*http.R
if err := checkRedirect(req, via, policy); err != nil {
return err
}
// Strip before the caller's hook so it observes what will actually
// be sent, and again afterwards so a hook of the common "preserve
// my headers across redirects" shape - which copies from via[0],
// the original unsanitized request - cannot reinstate them.
// Carrying credentials across an origin boundary is deliberately
// unsupported.
stripCredentials(req, via)
if next != nil {
return next(req, via)
if err := next(req, via); err != nil {
return err
}
}
stripCredentials(req, via)
return nil
}
}
// safeHeaders lists the headers go-git sets itself, none of which can carry a
// caller credential. stripCredentials keeps only these when a redirect leaves
// the credential's origin. Adding a name here makes it forwardable across an
// origin boundary - do not add anything a caller can put a secret in.
//
// This narrows rather than eliminates the exposure: an AuthMethod that writes
// a credential into one of these names directly - for example
// Header.Set("User-Agent", "token "+secret) - still survives a cross-origin
// redirect. Such a value is also sent to the origin and to any proxy in path,
// so it should not be placed there whether or not a redirect follows.
var safeHeaders = map[string]struct{}{
"User-Agent": {},
"Host": {},
"Accept": {},
"Content-Type": {},
"Content-Length": {},
}
func filterHeaders(h http.Header) http.Header {
filtered := make(http.Header)
for key, values := range h {
if _, ok := safeHeaders[http.CanonicalHeaderKey(key)]; ok {
filtered[key] = values
}
}
return filtered
}
// stripCredentials removes credentials from req once the redirect chain has
// left the origin of the original, credential-bearing request.
//
// CheckRedirect is the only hook that runs while a redirected request's
// headers are still mutable: http.Client.Do performs the entire chain
// internally, so anything the transport does after Do returns - including
// ModifyEndpointIfRedirect - is too late for the hops themselves.
//
// Two subtleties:
//
// - net/http rebuilds every redirect request from the original request's
// headers before calling this, so a header removed at one hop reappears
// at the next. The decision is therefore recomputed per hop.
// - The decision is sticky: once the chain has left the origin, credentials
// stay gone even if a later hop returns to it. Stickiness is derived from
// via rather than stored, because this closure is shared across a
// session's requests.
//
// Stripping keeps only the headers go-git sets itself (safeHeaders). An
// allowlist is used rather than a list of credential header names because
// caller credentials arrive under names that cannot be enumerated -
// PRIVATE-TOKEN, X-Api-Key, gateway headers - which is exactly what
// net/http's fixed list of sensitive header names gets wrong. It is also
// immune to header-name canonicalisation: an AuthMethod that writes a raw map
// key is still removed.
func stripCredentials(req *http.Request, via []*http.Request) {
if len(via) == 0 {
return
}
// net/http sets a URL on every request it builds, and req.URL is non-nil
// by construction: checkRedirect dereferences req.URL.Scheme on each path
// that returns nil, so it runs first or not at all. This nil check and
// the two in crossedOrigin are defensive, against a synthetic caller.
// Each treats a URL it cannot read as an origin crossing; removing one
// panics in canonicalHost rather than leaking.
if origin := via[0].URL; origin != nil && !crossedOrigin(origin, req, via) {
return
}
req.Header = filterHeaders(req.Header)
if req.URL != nil {
req.URL.User = nil
}
}
// crossedOrigin reports whether any hop so far, including the pending one, has
// left origin.
func crossedOrigin(origin *url.URL, req *http.Request, via []*http.Request) bool {
if req.URL == nil || !credentialsMayFollow(origin, req.URL) {
return true
}
for _, prev := range via[1:] {
if prev.URL == nil || !credentialsMayFollow(origin, prev.URL) {
return true
}
}
return false
}
// redactedURL returns the string form of u with the userinfo password
// replaced, for use in error messages. (*url.URL).String() renders the
// password verbatim, and request URLs are built from the endpoint, which
// carries whatever credentials the caller put in the clone URL.
func redactedURL(u *url.URL) string {
if u == nil {
return ""
}
if u.User == nil {
return u.String()
}
if _, hasPassword := u.User.Password(); !hasPassword {
return u.String()
}
redacted := *u
redacted.User = url.UserPassword(u.User.Username(), "REDACTED")
return redacted.String()
}
// redactedRawURL is redactedURL for a string that may not parse. Request URLs
// are assembled from Endpoint.String(), which re-emits Endpoint.Path raw, so a
// path holding a stray percent produces a string url.Parse rejects. url.Parse
// reports the input verbatim and applies no redaction of its own; only
// http.Client strips a password, and only from errors it raises itself.
func redactedRawURL(raw string) string {
i := strings.Index(raw, "://")
if i < 0 {
return raw
}
authority := raw[i+3:]
if end := strings.IndexByte(authority, '/'); end >= 0 {
authority = authority[:end]
}
at := strings.LastIndexByte(authority, '@')
if at < 0 {
return raw
}
colon := strings.IndexByte(authority[:at], ':')
if colon < 0 {
// Username only, left alone, as redactedURL leaves it.
return raw
}
return raw[:i+3+colon+1] + "REDACTED" + raw[i+3+at:]
}
// newRequest wraps http.NewRequest so that a URL it cannot parse does not
// reach the caller with the endpoint's credentials still in it.
func newRequest(method, rawURL string, body io.Reader) (*http.Request, error) {
req, err := http.NewRequest(method, rawURL, body)
if err != nil {
var uerr *url.Error
if errors.As(err, &uerr) {
uerr.URL = redactedRawURL(uerr.URL)
}
return nil, err
}
return req, nil
}
func checkRedirect(req *http.Request, via []*http.Request, policy RedirectPolicy) error {
// CheckRedirect is the only hook that runs before the next hop leaves
// the client. ModifyEndpointIfRedirect inspects the chain after
// client.Do has followed all of it, so a hop rejected there has already
// carried the request headers to its server.
//
// The wording matches the message ModifyEndpointIfRedirect produces for
// the same hop, which this check reaches first.
if len(via) != 0 {
// A hop whose scheme cannot be read cannot be shown not to have been
// https, so it is assumed to have been, and a cleartext target is
// rejected. Skipping the comparison instead would let an
// undeterminable hop turn the check off, which is the wrong default
// for a credential control; crossedOrigin fails closed the same way.
// An empty scheme is as unreadable as a nil URL, so both take the
// assumed-https default.
//
// The comparisons fold case because a scheme is case-insensitive per
// RFC 3986. net/url lowercases what it parses, so only a hand-built
// URL reaches here uppercased - the same synthetic caller the nil
// checks guard against - and for that caller "HTTPS" to "http" is
// still a downgrade.
prevScheme := "https"
if prev := via[len(via)-1]; prev.URL != nil && prev.URL.Scheme != "" {
prevScheme = prev.URL.Scheme
}
if strings.EqualFold(prevScheme, "https") && strings.EqualFold(req.URL.Scheme, "http") {
return fmt.Errorf("http redirect: changes scheme from %q to %q: %s",
prevScheme, req.URL.Scheme, redactedURL(req.URL))
}
}
switch policy {
case FollowRedirects:
case NoFollowRedirects:
return fmt.Errorf("http redirect: redirects disabled to %s", req.URL)
return fmt.Errorf("http redirect: redirects disabled to %s", redactedURL(req.URL))
case "", FollowInitialRedirects:
if !isInitialRequest(req) {
return fmt.Errorf("http redirect: redirect on non-initial request to %s", req.URL)
return fmt.Errorf("http redirect: redirect on non-initial request to %s", redactedURL(req.URL))
}
default:
return fmt.Errorf("http redirect: invalid redirect policy %q", policy)
}
if req.URL.Scheme != "http" && req.URL.Scheme != "https" {
// Folded for the same reason as the guard above: a scheme is
// case-insensitive per RFC 3986, so a spelling the downgrade check read
// as https must not be rejected here as a scheme go-git cannot speak.
if !strings.EqualFold(req.URL.Scheme, "http") && !strings.EqualFold(req.URL.Scheme, "https") {
return fmt.Errorf("http redirect: unsupported scheme %q", req.URL.Scheme)
}
if len(via) >= 10 {
@@ -481,6 +827,17 @@ func (*session) Close() error {
}
// AuthMethod is concrete implementation of common.AuthMethod for HTTP services
//
// Headers SetAuth adds are dropped when a redirect leaves the repository's
// origin: only the headers the transport sets itself survive that boundary.
// This applies to non-credential headers too, so an implementation that adds a
// trace or tenant header loses it on such a hop.
//
// That filter matches header names, not values. An implementation that writes
// a credential into a name the transport also uses - User-Agent, Host, Accept,
// Content-Type, Content-Length - has that value carried across the boundary
// with the name. Such a credential is also sent to the origin and to any proxy
// in path, so it should not be placed there whether or not a redirect follows.
type AuthMethod interface {
transport.AuthMethod
SetAuth(r *http.Request)
@@ -598,6 +955,6 @@ func (e *Err) StatusCode() int {
func (e *Err) Error() string {
return fmt.Sprintf("unexpected requesting %q status code: %d",
e.Response.Request.URL, e.Response.StatusCode,
redactedURL(e.Response.Request.URL), e.Response.StatusCode,
)
}
@@ -88,7 +88,7 @@ func (s *rpSession) doRequest(
body = content
}
req, err := http.NewRequest(method, url, body)
req, err := newRequest(method, url, body)
if err != nil {
return nil, plumbing.NewPermanentError(err)
}
+1 -1
View File
@@ -86,7 +86,7 @@ func (s *upSession) doRequest(
body = content
}
req, err := http.NewRequest(method, url, body)
req, err := newRequest(method, url, body)
if err != nil {
return nil, plumbing.NewPermanentError(err)
}
+1
View File
@@ -433,6 +433,7 @@ func dotGitCommonDirectory(fs billy.Filesystem) (commonDir billy.Filesystem, err
if err != nil {
return nil, err
}
defer ioutil.CheckClose(f, &err)
b, err := io.ReadAll(f)
if err != nil {
+57
View File
@@ -16,6 +16,7 @@ import (
"strings"
"time"
"github.com/go-git/go-git/v5/internal/pathutil"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/hash"
"github.com/go-git/go-git/v5/storage"
@@ -79,8 +80,52 @@ var (
// resolve outside the modules/ subtree, mirroring canonical Git's
// "ignoring suspicious submodule name" defence.
ErrModuleNameEscape = errors.New("submodule name escapes modules/ directory")
// ErrReferenceNameEscape is returned when a reference name would
// resolve outside its reference sub-tree once turned into a path
// under the .git directory (e.g. a name with a ".." component).
ErrReferenceNameEscape = errors.New("reference name escapes the reference storage")
)
// isPathSep reports whether r is a path separator in reference names.
// It treats both '/' and '\\' as separators to harden against cross-OS paths.
func isPathSep(r rune) bool { return r == '/' || r == '\\' }
// validReferenceName rejects reference names that cannot be safely turned into
// a path under the .git directory. A loose reference is stored verbatim at
// ".git/<name>", so a crafted name — for instance one advertised by a malicious
// remote — could climb out of its reference sub-tree and read, overwrite, or
// delete unrelated metadata such as .git/config.
//
// The storage-safety gate is plumbing.ReferenceName.IsSafe, mirroring Git's
// refname_is_safe: a name must be under refs/ without escaping it, or be a
// [A-Z_] pseudo-ref. This alone rejects absolute, drive-prefixed, escaping and
// single-level metadata names. On top of it, this adds filesystem-specific
// hardening that IsSafe's literal check does not cover: control characters, and
// components a case-insensitive/NTFS/HFS+ filesystem would fold back to "." or
// ".." (trailing dots/spaces, Alternate Data Streams, ignorable Unicode code
// points), delegated to pathutil.IsHFSDot and pathutil.IsNTFSDot with "." as
// the needle — as validSubmoduleName does — and run regardless of host OS.
func validReferenceName(name plumbing.ReferenceName) error {
if !name.IsSafe() {
return fmt.Errorf("%w: %q is not under refs/ nor a valid pseudo-ref", ErrReferenceNameEscape, string(name))
}
s := string(name)
for i := 0; i < len(s); i++ {
if s[i] < 0x20 || s[i] == 0x7f {
return fmt.Errorf("%w: %q", ErrReferenceNameEscape, s)
}
}
for _, part := range strings.FieldsFunc(s, isPathSep) {
// IsNTFSDot/IsHFSDot with a "." needle match ".." and its disguises
// but not a bare ".", so reject that component explicitly too.
if part == "." || pathutil.IsHFSDot(part, ".") || pathutil.IsNTFSDot(part, ".", "") {
return fmt.Errorf("%w: %q", ErrReferenceNameEscape, s)
}
}
return nil
}
// Options holds configuration for the storage.
type Options struct {
// ExclusiveAccess means that the filesystem is not modified externally
@@ -706,6 +751,10 @@ func (d *DotGit) checkReferenceAndTruncate(f billy.File, old *plumbing.Reference
}
func (d *DotGit) SetRef(r, old *plumbing.Reference) error {
if err := validReferenceName(r.Name()); err != nil {
return err
}
var content string
switch r.Type() {
case plumbing.SymbolicReference:
@@ -741,6 +790,10 @@ func (d *DotGit) Refs() ([]*plumbing.Reference, error) {
// Ref returns the reference for a given reference name.
func (d *DotGit) Ref(name plumbing.ReferenceName) (*plumbing.Reference, error) {
if err := validReferenceName(name); err != nil {
return nil, err
}
ref, err := d.readReferenceFile(".", name.String())
if err == nil {
return ref, nil
@@ -804,6 +857,10 @@ func (d *DotGit) packedRef(name plumbing.ReferenceName) (*plumbing.Reference, er
// RemoveRef removes a reference by name.
func (d *DotGit) RemoveRef(name plumbing.ReferenceName) error {
if err := validReferenceName(name); err != nil {
return err
}
path := d.fs.Join(".", name.String())
_, err := d.fs.Stat(path)
if err == nil {
+67
View File
@@ -583,6 +583,10 @@ func (w *Worktree) checkoutChangeSubmodule(name string,
return err
}
if err := w.clearBlockingSymlinks(name); err != nil {
return err
}
if err := w.Filesystem.MkdirAll(name, mode); err != nil {
return err
}
@@ -626,7 +630,70 @@ func (w *Worktree) checkoutChangeRegularFile(name string,
return nil
}
// clearBlockingSymlinks removes a symlink that is in the way of
// materialising name, so the checkout writes a real entry in its place
// instead of following the link out of the worktree. Two cases:
//
// - a leading directory component that is a symlink (e.g. "s" while
// writing "s/config", where "s" links to ".git"): OpenFile/MkdirAll
// would traverse it, so the write would land under the link's target.
// - the final component itself being a symlink (e.g. writing "s" while
// "s" links to ".git/config"): OpenFile with O_TRUNC, or Symlink,
// would follow/replace through it and clobber the target.
//
// A symlink can never be a legitimate parent of, or the destination for,
// a tracked entry, so removing it is always correct. This mirrors upstream
// Git's forced checkout, which unlinks a blocking symlink in the leading
// path (create_directories) and unlinks an existing entry before
// write_entry.
// https://github.com/git/git/blob/v2.54.0/entry.c#L50
func (w *Worktree) clearBlockingSymlinks(name string) error {
var dirs []string
for dir := filepath.Dir(name); dir != "." && dir != "" && dir != string(filepath.Separator); dir = filepath.Dir(dir) {
dirs = append(dirs, dir)
}
// Leading components, shallowest-first: removing the shallowest symlink
// invalidates every component beneath it, so a single removal is enough.
for i := len(dirs) - 1; i >= 0; i-- {
fi, err := w.Filesystem.Lstat(dirs[i])
if err != nil {
// A missing component is created as a real directory by the
// checkout. Any other error means we cannot tell whether it is
// a symlink, so surface it instead of leaving a blocking link in
// place and failing later in a harder-to-diagnose way.
if os.IsNotExist(err) {
continue
}
return err
}
if fi.Mode()&os.ModeSymlink != 0 {
return w.Filesystem.Remove(dirs[i])
}
}
// Final component: an existing symlink here would be followed by the
// subsequent OpenFile/Symlink/MkdirAll, so replace it.
fi, err := w.Filesystem.Lstat(name)
if err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
if fi.Mode()&os.ModeSymlink != 0 {
return w.Filesystem.Remove(name)
}
return nil
}
func (w *Worktree) checkoutFile(f *object.File) (err error) {
// checkoutFile is the materialisation boundary for tracked entries.
// Remove any blocking symlink first so the subsequent OpenFile or
// Symlink call writes the entry itself instead of following a planted
// final-component link in the underlying filesystem.
if err := w.clearBlockingSymlinks(f.Name); err != nil {
return err
}
mode, err := f.Mode.ToOSFileMode()
if err != nil {
return
+100 -15
View File
@@ -35,10 +35,25 @@ func defaultProtectNTFS() bool {
return true
}
// worktreeFilesystem wraps a billy.Filesystem and validates every path passed
// to a mutating operation. This prevents writing to, or deleting from,
// dangerous locations (e.g. .git/*, ../) regardless of which worktree
// code path triggers the operation.
// worktreeFilesystem wraps a billy.Filesystem and validates every path it
// is handed, so worktree operations cannot use dangerous paths at the
// boundary. Two layers apply:
//
// - validPath rejects dangerous path *strings*: .git and its HFS+/NTFS
// variants, "..", control characters, volume names.
// - validNoLeadingSymlink rejects paths whose leading directories
// already exist on disk as symlinks, so a write or delete cannot
// follow a planted link out of the tree.
//
// Both layers run on every mutating operation (validWritePath) and every
// read (validReadPath). Chroot additionally refuses a symlink as the final
// component, so a sub-filesystem such as a submodule worktree cannot be
// scoped to a redirected target.
//
// The wrapper intentionally stops at leading-component traversal. Callers
// that need final-component no-follow semantics for materialisation
// (checkoutFile) enforce that directly by removing the blocking symlink
// before opening the destination path.
type worktreeFilesystem struct {
billy.Filesystem
protectNTFS bool
@@ -50,7 +65,7 @@ func newWorktreeFilesystem(fs billy.Filesystem, protectNTFS, protectHFS bool) *w
}
func (sfs *worktreeFilesystem) Create(filename string) (billy.File, error) {
if err := sfs.validPath(filename); err != nil {
if err := sfs.validWritePath(filename); err != nil {
return nil, fmt.Errorf("create: %w", err)
}
return sfs.Filesystem.Create(filename)
@@ -64,7 +79,7 @@ func (sfs *worktreeFilesystem) Open(filename string) (billy.File, error) {
}
func (sfs *worktreeFilesystem) OpenFile(filename string, flag int, perm os.FileMode) (billy.File, error) {
if err := sfs.validPath(filename); err != nil {
if err := sfs.validWritePath(filename); err != nil {
return nil, fmt.Errorf("openfile: %w", err)
}
return sfs.Filesystem.OpenFile(filename, flag, perm)
@@ -78,14 +93,14 @@ func (sfs *worktreeFilesystem) Stat(filename string) (os.FileInfo, error) {
}
func (sfs *worktreeFilesystem) Remove(filename string) error {
if err := sfs.validPath(filename); err != nil {
if err := sfs.validWritePath(filename); err != nil {
return fmt.Errorf("remove: %w", err)
}
return sfs.Filesystem.Remove(filename)
}
func (sfs *worktreeFilesystem) Rename(from, to string) error {
if err := sfs.validPath(from, to); err != nil {
if err := sfs.validWritePath(from, to); err != nil {
return fmt.Errorf("rename: %w", err)
}
return sfs.Filesystem.Rename(from, to)
@@ -106,7 +121,7 @@ func (sfs *worktreeFilesystem) Lstat(filename string) (os.FileInfo, error) {
}
func (sfs *worktreeFilesystem) Symlink(target, link string) error {
if err := sfs.validPath(link); err != nil {
if err := sfs.validWritePath(link); err != nil {
return fmt.Errorf("symlink: %w", err)
}
if err := sfs.validSymlinkName(link); err != nil {
@@ -131,7 +146,7 @@ func (sfs *worktreeFilesystem) MkdirAll(path string, perm os.FileMode) error {
if path == "" || path == "." || path == "/" {
return nil
}
if err := sfs.validPath(path); err != nil {
if err := sfs.validWritePath(path); err != nil {
return fmt.Errorf("mkdirall: %w", err)
}
return sfs.Filesystem.MkdirAll(path, perm)
@@ -145,18 +160,39 @@ func (sfs *worktreeFilesystem) Chroot(path string) (billy.Filesystem, error) {
if err := sfs.validReadPath(path); err != nil {
return nil, fmt.Errorf("chroot: %w", err)
}
// Chroot scopes a sub-filesystem to path, so the final component must
// be a real directory too: a symlink there would silently redirect the
// scope (e.g. a submodule worktree) to a target outside the tree. This
// is the "valid path, wrong target" case that validNoLeadingSymlink,
// which only inspects leading components, does not cover.
//
// A non-existent target is fine: Chroot creates it as a real
// directory. Any other Lstat error means we cannot prove the target
// is not a symlink, so fail closed rather than scope through it.
if fi, err := sfs.Filesystem.Lstat(path); err != nil {
if !os.IsNotExist(err) {
return nil, fmt.Errorf("chroot: cannot stat %q: %w", path, err)
}
} else if fi.Mode()&os.ModeSymlink != 0 {
return nil, fmt.Errorf("chroot: invalid path %q: is a symlink", path)
}
return sfs.Filesystem.Chroot(path)
}
// validReadPath is like validPath but treats the empty string and "." as
// valid references to the worktree root. Read-side operations on the root
// (e.g. ReadDir(""), Lstat(".")) are legitimate; mutating the root itself
// is not, so write-side operations continue to use validPath directly.
// validReadPath is like validWritePath but treats the empty string and "."
// as valid references to the worktree root. Read-side operations on the
// root (e.g. ReadDir(""), Lstat(".")) are legitimate. Mutating the root
// itself is not, so write-side operations reject it via validPath. Reads
// are still refused through a leading symlink, so the wrapper never
// follows a planted link even on the read surface.
func (sfs *worktreeFilesystem) validReadPath(p string) error {
if p == "" || p == "." || p == "/" {
return nil
}
return sfs.validPath(p)
if err := sfs.validPath(p); err != nil {
return err
}
return sfs.validNoLeadingSymlink(p)
}
var errUnsupportedOperation = errors.New("unsupported operation")
@@ -232,6 +268,55 @@ func (sfs *worktreeFilesystem) validPath(paths ...string) error {
return nil
}
// validWritePath validates paths for mutating operations. It layers the
// filesystem-state check validNoLeadingSymlink on top of the string-only
// checks in validPath, so a write can neither name a dangerous path nor
// reach one by traversing an existing symlink. Every mutating method on
// the wrapper funnels through here, so the leading-symlink invariant holds
// for all worktree writers without each call site having to remember it.
func (sfs *worktreeFilesystem) validWritePath(paths ...string) error {
if err := sfs.validPath(paths...); err != nil {
return err
}
return sfs.validNoLeadingSymlink(paths...)
}
// validNoLeadingSymlink rejects paths whose leading directory components
// resolve through a symlink that already exists on the underlying
// filesystem. validPath guards the path string. This guards the on-disk
// state, so a write or delete cannot reach outside the worktree by
// traversing a symlink that a tree or an earlier step left in place.
//
// This is the fail-closed backstop for the whole class. Callers that want
// upstream's replace-and-continue behaviour (checkout) remove the blocking
// symlink first via clearBlockingSymlinks, so no symlink remains when the
// write reaches the wrapper. Callers that do not get a safe error,
// matching upstream Git refusing rather than following the link. See
// has_symlink_leading_path (symlinks.c) and the check_leading_path guard
// in unlink_entry (entry.c).
func (sfs *worktreeFilesystem) validNoLeadingSymlink(paths ...string) error {
for _, p := range paths {
for dir := filepath.Dir(p); dir != "." && dir != "" && dir != string(filepath.Separator); dir = filepath.Dir(dir) {
fi, err := sfs.Filesystem.Lstat(dir)
if err != nil {
// A missing ancestor is materialised as a real directory,
// so it cannot be a symlink and is safe to skip. Any other
// error (permission, I/O) means we cannot prove the
// component is not a symlink, so fail closed rather than
// let the operation traverse an unverified component.
if os.IsNotExist(err) {
continue
}
return fmt.Errorf("invalid path %q: cannot stat leading component %q: %w", p, dir, err)
}
if fi.Mode()&os.ModeSymlink != 0 {
return fmt.Errorf("invalid path %q: leading component %q is a symlink", p, dir)
}
}
}
return nil
}
// validSymlinkName checks the per-component name of a symlink for
// dotfile names that attackers can use to trick a checkout into
// writing a dangerous symlink. Each path component is compared
+1 -1
View File
@@ -371,7 +371,7 @@ func (w *Worktree) doAdd(path string, ignorePattern []gitignore.Pattern, skipSta
}
}
path = filepath.Clean(path)
path = filepath.ToSlash(filepath.Clean(path))
if err != nil || !fi.IsDir() {
added, h, err = w.doAddFile(idx, s, path, ignorePattern)
-24
View File
@@ -1,24 +0,0 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
-57
View File
@@ -1,57 +0,0 @@
version: 2
builds:
-
id: "cpuid"
binary: cpuid
main: ./cmd/cpuid/main.go
env:
- CGO_ENABLED=0
flags:
- -ldflags=-s -w
goos:
- aix
- linux
- freebsd
- netbsd
- windows
- darwin
goarch:
- 386
- amd64
- arm64
goarm:
- 7
archives:
-
id: cpuid
name_template: "cpuid-{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
format_overrides:
- goos: windows
format: zip
files:
- LICENSE
checksum:
name_template: 'checksums.txt'
changelog:
sort: asc
filters:
exclude:
- '^doc:'
- '^docs:'
- '^test:'
- '^tests:'
- '^Update\sREADME.md'
nfpms:
-
file_name_template: "cpuid_package_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
vendor: Klaus Post
homepage: https://github.com/klauspost/cpuid
maintainer: Klaus Post <klauspost@gmail.com>
description: CPUID Tool
license: BSD 3-Clause
formats:
- deb
- rpm
-35
View File
@@ -1,35 +0,0 @@
Developer Certificate of Origin
Version 1.1
Copyright (C) 2015- Klaus Post & Contributors.
Email: klauspost@gmail.com
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
-22
View File
@@ -1,22 +0,0 @@
The MIT License (MIT)
Copyright (c) 2015 Klaus Post
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-512
View File
@@ -1,512 +0,0 @@
# cpuid
Package cpuid provides information about the CPU running the current program.
CPU features are detected on startup, and kept for fast access through the life of the application.
Currently x86 / x64 (AMD64/i386) and ARM (ARM64) is supported, and no external C (cgo) code is used, which should make the library very easy to use.
You can access the CPU information by accessing the shared CPU variable of the cpuid library.
Package home: https://github.com/klauspost/cpuid
[![PkgGoDev](https://pkg.go.dev/badge/github.com/klauspost/cpuid)](https://pkg.go.dev/github.com/klauspost/cpuid/v2)
[![Go](https://github.com/klauspost/cpuid/actions/workflows/go.yml/badge.svg)](https://github.com/klauspost/cpuid/actions/workflows/go.yml)
## installing
`go get -u github.com/klauspost/cpuid/v2` using modules.
Drop `v2` for others.
Installing binary:
`go install github.com/klauspost/cpuid/v2/cmd/cpuid@latest`
Or download binaries from release page: https://github.com/klauspost/cpuid/releases
### Homebrew
For macOS/Linux users, you can install via [brew](https://brew.sh/)
```sh
$ brew install cpuid
```
## example
```Go
package main
import (
"fmt"
"strings"
. "github.com/klauspost/cpuid/v2"
)
func main() {
// Print basic CPU information:
fmt.Println("Name:", CPU.BrandName)
fmt.Println("PhysicalCores:", CPU.PhysicalCores)
fmt.Println("ThreadsPerCore:", CPU.ThreadsPerCore)
fmt.Println("LogicalCores:", CPU.LogicalCores)
fmt.Println("Family", CPU.Family, "Model:", CPU.Model, "Vendor ID:", CPU.VendorID)
fmt.Println("Features:", strings.Join(CPU.FeatureSet(), ","))
fmt.Println("Cacheline bytes:", CPU.CacheLine)
fmt.Println("L1 Data Cache:", CPU.Cache.L1D, "bytes")
fmt.Println("L1 Instruction Cache:", CPU.Cache.L1I, "bytes")
fmt.Println("L2 Cache:", CPU.Cache.L2, "bytes")
fmt.Println("L3 Cache:", CPU.Cache.L3, "bytes")
fmt.Println("Frequency", CPU.Hz, "hz")
// Test if we have these specific features:
if CPU.Supports(SSE, SSE2) {
fmt.Println("We have Streaming SIMD 2 Extensions")
}
}
```
Sample output:
```
>go run main.go
Name: AMD Ryzen 9 3950X 16-Core Processor
PhysicalCores: 16
ThreadsPerCore: 2
LogicalCores: 32
Family 23 Model: 113 Vendor ID: AMD
Features: ADX,AESNI,AVX,AVX2,BMI1,BMI2,CLMUL,CMOV,CX16,F16C,FMA3,HTT,HYPERVISOR,LZCNT,MMX,MMXEXT,NX,POPCNT,RDRAND,RDSEED,RDTSCP,SHA,SSE,SSE2,SSE3,SSE4,SSE42,SSE4A,SSSE3
Cacheline bytes: 64
L1 Data Cache: 32768 bytes
L1 Instruction Cache: 32768 bytes
L2 Cache: 524288 bytes
L3 Cache: 16777216 bytes
Frequency 0 hz
We have Streaming SIMD 2 Extensions
```
# usage
The `cpuid.CPU` provides access to CPU features. Use `cpuid.CPU.Supports()` to check for CPU features.
A faster `cpuid.CPU.Has()` is provided which will usually be inlined by the gc compiler.
To test a larger number of features, they can be combined using `f := CombineFeatures(CMOV, CMPXCHG8, X87, FXSR, MMX, SYSCALL, SSE, SSE2)`, etc.
This can be using with `cpuid.CPU.HasAll(f)` to quickly test if all features are supported.
Note that for some cpu/os combinations some features will not be detected.
`amd64` has rather good support and should work reliably on all platforms.
Note that hypervisors may not pass through all CPU features through to the guest OS,
so even if your host supports a feature it may not be visible on guests.
## arm64 feature detection
Not all operating systems provide ARM features directly
and there is no safe way to do so for the rest.
Currently `arm64/linux` and `arm64/freebsd` should be quite reliable.
`arm64/darwin` adds features expected from the M1 processor, but a lot remains undetected.
A `DetectARM()` can be used if you are able to control your deployment,
it will detect CPU features, but may crash if the OS doesn't intercept the calls.
A `-cpu.arm` flag for detecting unsafe ARM features can be added. See below.
Note that currently only features are detected on ARM,
no additional information is currently available.
## flags
It is possible to add flags that affects cpu detection.
For this the `Flags()` command is provided.
This must be called *before* `flag.Parse()` AND after the flags have been parsed `Detect()` must be called.
This means that any detection used in `init()` functions will not contain these flags.
Example:
```Go
package main
import (
"flag"
"fmt"
"strings"
"github.com/klauspost/cpuid/v2"
)
func main() {
cpuid.Flags()
flag.Parse()
cpuid.Detect()
// Test if we have these specific features:
if cpuid.CPU.Supports(cpuid.SSE, cpuid.SSE2) {
fmt.Println("We have Streaming SIMD 2 Extensions")
}
}
```
## commandline
Download as binary from: https://github.com/klauspost/cpuid/releases
Install from source:
`go install github.com/klauspost/cpuid/v2/cmd/cpuid@latest`
### Example
```
λ cpuid
Name: AMD Ryzen 9 3950X 16-Core Processor
Vendor String: AuthenticAMD
Vendor ID: AMD
PhysicalCores: 16
Threads Per Core: 2
Logical Cores: 32
CPU Family 23 Model: 113
Features: ADX,AESNI,AVX,AVX2,BMI1,BMI2,CLMUL,CLZERO,CMOV,CMPXCHG8,CPBOOST,CX16,F16C,FMA3,FXSR,FXSROPT,HTT,HYPERVISOR,LAHF,LZCNT,MCAOVERFLOW,MMX,MMXEXT,MOVBE,NX,OSXSAVE,POPCNT,RDRAND,RDSEED,RDTSCP,SCE,SHA,SSE,SSE2,SSE3,SSE4,SSE42,SSE4A,SSSE3,SUCCOR,X87,XSAVE
Microarchitecture level: 3
Cacheline bytes: 64
L1 Instruction Cache: 32768 bytes
L1 Data Cache: 32768 bytes
L2 Cache: 524288 bytes
L3 Cache: 16777216 bytes
```
### JSON Output:
```
λ cpuid --json
{
"BrandName": "AMD Ryzen 9 3950X 16-Core Processor",
"VendorID": 2,
"VendorString": "AuthenticAMD",
"PhysicalCores": 16,
"ThreadsPerCore": 2,
"LogicalCores": 32,
"Family": 23,
"Model": 113,
"CacheLine": 64,
"Hz": 0,
"BoostFreq": 0,
"Cache": {
"L1I": 32768,
"L1D": 32768,
"L2": 524288,
"L3": 16777216
},
"SGX": {
"Available": false,
"LaunchControl": false,
"SGX1Supported": false,
"SGX2Supported": false,
"MaxEnclaveSizeNot64": 0,
"MaxEnclaveSize64": 0,
"EPCSections": null
},
"Features": [
"ADX",
"AESNI",
"AVX",
"AVX2",
"BMI1",
"BMI2",
"CLMUL",
"CLZERO",
"CMOV",
"CMPXCHG8",
"CPBOOST",
"CX16",
"F16C",
"FMA3",
"FXSR",
"FXSROPT",
"HTT",
"HYPERVISOR",
"LAHF",
"LZCNT",
"MCAOVERFLOW",
"MMX",
"MMXEXT",
"MOVBE",
"NX",
"OSXSAVE",
"POPCNT",
"RDRAND",
"RDSEED",
"RDTSCP",
"SCE",
"SHA",
"SSE",
"SSE2",
"SSE3",
"SSE4",
"SSE42",
"SSE4A",
"SSSE3",
"SUCCOR",
"X87",
"XSAVE"
],
"X64Level": 3
}
```
### Check CPU microarch level
```
λ cpuid --check-level=3
2022/03/18 17:04:40 AMD Ryzen 9 3950X 16-Core Processor
2022/03/18 17:04:40 Microarchitecture level 3 is supported. Max level is 3.
Exit Code 0
λ cpuid --check-level=4
2022/03/18 17:06:18 AMD Ryzen 9 3950X 16-Core Processor
2022/03/18 17:06:18 Microarchitecture level 4 not supported. Max level is 3.
Exit Code 1
```
## Available flags
### x86 & amd64
| Feature Flag | Description |
|--------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ADX | Intel ADX (Multi-Precision Add-Carry Instruction Extensions) |
| AESNI | Advanced Encryption Standard New Instructions |
| AMD3DNOW | AMD 3DNOW |
| AMD3DNOWEXT | AMD 3DNowExt |
| AMXBF16 | Tile computational operations on BFLOAT16 numbers |
| AMXINT8 | Tile computational operations on 8-bit integers |
| AMXFP16 | Tile computational operations on FP16 numbers |
| AMXFP8 | Tile computational operations on FP8 numbers |
| AMXCOMPLEX | Tile computational operations on complex numbers |
| AMXTILE | Tile architecture |
| AMXTF32 | Matrix Multiplication of TF32 Tiles into Packed Single Precision Tile |
| AMXTRANSPOSE | Tile multiply where the first operand is transposed |
| APX_F | Intel APX |
| AVX | AVX functions |
| AVX10 | If set the Intel AVX10 Converged Vector ISA is supported |
| AVX10_128 | If set indicates that AVX10 128-bit vector support is present |
| AVX10_256 | If set indicates that AVX10 256-bit vector support is present |
| AVX10_512 | If set indicates that AVX10 512-bit vector support is present |
| AVX2 | AVX2 functions |
| AVX512BF16 | AVX-512 BFLOAT16 Instructions |
| AVX512BITALG | AVX-512 Bit Algorithms |
| AVX512BW | AVX-512 Byte and Word Instructions |
| AVX512CD | AVX-512 Conflict Detection Instructions |
| AVX512DQ | AVX-512 Doubleword and Quadword Instructions |
| AVX512ER | AVX-512 Exponential and Reciprocal Instructions |
| AVX512F | AVX-512 Foundation |
| AVX512FP16 | AVX-512 FP16 Instructions |
| AVX512IFMA | AVX-512 Integer Fused Multiply-Add Instructions |
| AVX512PF | AVX-512 Prefetch Instructions |
| AVX512VBMI | AVX-512 Vector Bit Manipulation Instructions |
| AVX512VBMI2 | AVX-512 Vector Bit Manipulation Instructions, Version 2 |
| AVX512VL | AVX-512 Vector Length Extensions |
| AVX512VNNI | AVX-512 Vector Neural Network Instructions |
| AVX512VP2INTERSECT | AVX-512 Intersect for D/Q |
| AVX512VPOPCNTDQ | AVX-512 Vector Population Count Doubleword and Quadword |
| AVXIFMA | AVX-IFMA instructions |
| AVXNECONVERT | AVX-NE-CONVERT instructions |
| AVXSLOW | Indicates the CPU performs 2 128 bit operations instead of one |
| AVXVNNI | AVX (VEX encoded) VNNI neural network instructions |
| AVXVNNIINT8 | AVX-VNNI-INT8 instructions |
| AVXVNNIINT16 | AVX-VNNI-INT16 instructions |
| BHI_CTRL | Branch History Injection and Intra-mode Branch Target Injection / CVE-2022-0001, CVE-2022-0002 / INTEL-SA-00598 |
| BMI1 | Bit Manipulation Instruction Set 1 |
| BMI2 | Bit Manipulation Instruction Set 2 |
| CETIBT | Intel CET Indirect Branch Tracking |
| CETSS | Intel CET Shadow Stack |
| CLDEMOTE | Cache Line Demote |
| CLMUL | Carry-less Multiplication |
| CLZERO | CLZERO instruction supported |
| CMOV | i686 CMOV |
| CMPCCXADD | CMPCCXADD instructions |
| CMPSB_SCADBS_SHORT | Fast short CMPSB and SCASB |
| CMPXCHG8 | CMPXCHG8 instruction |
| CPBOOST | Core Performance Boost |
| CPPC | AMD: Collaborative Processor Performance Control |
| CX16 | CMPXCHG16B Instruction |
| EFER_LMSLE_UNS | AMD: =Core::X86::Msr::EFER[LMSLE] is not supported, and MBZ |
| ENQCMD | Enqueue Command |
| ERMS | Enhanced REP MOVSB/STOSB |
| F16C | Half-precision floating-point conversion |
| FLUSH_L1D | Flush L1D cache |
| FMA3 | Intel FMA 3. Does not imply AVX. |
| FMA4 | Bulldozer FMA4 functions |
| FP128 | AMD: When set, the internal FP/SIMD execution datapath is 128-bits wide |
| FP256 | AMD: When set, the internal FP/SIMD execution datapath is 256-bits wide |
| FSRM | Fast Short Rep Mov |
| FXSR | FXSAVE, FXRESTOR instructions, CR4 bit 9 |
| FXSROPT | FXSAVE/FXRSTOR optimizations |
| GFNI | Galois Field New Instructions. May require other features (AVX, AVX512VL,AVX512F) based on usage. |
| HLE | Hardware Lock Elision |
| HRESET | If set CPU supports history reset and the IA32_HRESET_ENABLE MSR |
| HTT | Hyperthreading (enabled) |
| HWA | Hardware assert supported. Indicates support for MSRC001_10 |
| HYBRID_CPU | This part has CPUs of more than one type. |
| HYPERVISOR | This bit has been reserved by Intel & AMD for use by hypervisors |
| IA32_ARCH_CAP | IA32_ARCH_CAPABILITIES MSR (Intel) |
| IA32_CORE_CAP | IA32_CORE_CAPABILITIES MSR |
| IBPB | Indirect Branch Restricted Speculation (IBRS) and Indirect Branch Predictor Barrier (IBPB) |
| IBRS | AMD: Indirect Branch Restricted Speculation |
| IBRS_PREFERRED | AMD: IBRS is preferred over software solution |
| IBRS_PROVIDES_SMP | AMD: IBRS provides Same Mode Protection |
| IBS | Instruction Based Sampling (AMD) |
| IBSBRNTRGT | Instruction Based Sampling Feature (AMD) |
| IBSFETCHSAM | Instruction Based Sampling Feature (AMD) |
| IBSFFV | Instruction Based Sampling Feature (AMD) |
| IBSOPCNT | Instruction Based Sampling Feature (AMD) |
| IBSOPCNTEXT | Instruction Based Sampling Feature (AMD) |
| IBSOPSAM | Instruction Based Sampling Feature (AMD) |
| IBSRDWROPCNT | Instruction Based Sampling Feature (AMD) |
| IBSRIPINVALIDCHK | Instruction Based Sampling Feature (AMD) |
| IBS_FETCH_CTLX | AMD: IBS fetch control extended MSR supported |
| IBS_OPDATA4 | AMD: IBS op data 4 MSR supported |
| IBS_OPFUSE | AMD: Indicates support for IbsOpFuse |
| IBS_PREVENTHOST | Disallowing IBS use by the host supported |
| IBS_ZEN4 | Fetch and Op IBS support IBS extensions added with Zen4 |
| IDPRED_CTRL | IPRED_DIS |
| INT_WBINVD | WBINVD/WBNOINVD are interruptible. |
| INVLPGB | NVLPGB and TLBSYNC instruction supported |
| KEYLOCKER | Key locker |
| KEYLOCKERW | Key locker wide |
| LAHF | LAHF/SAHF in long mode |
| LAM | If set, CPU supports Linear Address Masking |
| LBRVIRT | LBR virtualization |
| LZCNT | LZCNT instruction |
| MCAOVERFLOW | MCA overflow recovery support. |
| MCDT_NO | Processor do not exhibit MXCSR Configuration Dependent Timing behavior and do not need to mitigate it. |
| MCOMMIT | MCOMMIT instruction supported |
| MD_CLEAR | VERW clears CPU buffers |
| MMX | standard MMX |
| MMXEXT | SSE integer functions or AMD MMX ext |
| MOVBE | MOVBE instruction (big-endian) |
| MOVDIR64B | Move 64 Bytes as Direct Store |
| MOVDIRI | Move Doubleword as Direct Store |
| MOVSB_ZL | Fast Zero-Length MOVSB |
| MPX | Intel MPX (Memory Protection Extensions) |
| MOVU | MOVU SSE instructions are more efficient and should be preferred to SSE MOVL/MOVH. MOVUPS is more efficient than MOVLPS/MOVHPS. MOVUPD is more efficient than MOVLPD/MOVHPD |
| MSRIRC | Instruction Retired Counter MSR available |
| MSRLIST | Read/Write List of Model Specific Registers |
| MSR_PAGEFLUSH | Page Flush MSR available |
| NRIPS | Indicates support for NRIP save on VMEXIT |
| NX | NX (No-Execute) bit |
| OSXSAVE | XSAVE enabled by OS |
| PCONFIG | PCONFIG for Intel Multi-Key Total Memory Encryption |
| POPCNT | POPCNT instruction |
| PPIN | AMD: Protected Processor Inventory Number support. Indicates that Protected Processor Inventory Number (PPIN) capability can be enabled |
| PREFETCHI | PREFETCHIT0/1 instructions |
| PSFD | Predictive Store Forward Disable |
| RDPRU | RDPRU instruction supported |
| RDRAND | RDRAND instruction is available |
| RDSEED | RDSEED instruction is available |
| RDTSCP | RDTSCP Instruction |
| RRSBA_CTRL | Restricted RSB Alternate |
| RTM | Restricted Transactional Memory |
| RTM_ALWAYS_ABORT | Indicates that the loaded microcode is forcing RTM abort. |
| SERIALIZE | Serialize Instruction Execution |
| SEV | AMD Secure Encrypted Virtualization supported |
| SEV_64BIT | AMD SEV guest execution only allowed from a 64-bit host |
| SEV_ALTERNATIVE | AMD SEV Alternate Injection supported |
| SEV_DEBUGSWAP | Full debug state swap supported for SEV-ES guests |
| SEV_ES | AMD SEV Encrypted State supported |
| SEV_RESTRICTED | AMD SEV Restricted Injection supported |
| SEV_SNP | AMD SEV Secure Nested Paging supported |
| SGX | Software Guard Extensions |
| SGXLC | Software Guard Extensions Launch Control |
| SGXPQC | Software Guard Extensions 256-bit Encryption |
| SHA | Intel SHA Extensions |
| SME | AMD Secure Memory Encryption supported |
| SME_COHERENT | AMD Hardware cache coherency across encryption domains enforced |
| SM3_X86 | SM3 instructions |
| SM4_X86 | SM4 instructions |
| SPEC_CTRL_SSBD | Speculative Store Bypass Disable |
| SRBDS_CTRL | SRBDS mitigation MSR available |
| SSE | SSE functions |
| SSE2 | P4 SSE functions |
| SSE3 | Prescott SSE3 functions |
| SSE4 | Penryn SSE4.1 functions |
| SSE42 | Nehalem SSE4.2 functions |
| SSE4A | AMD Barcelona microarchitecture SSE4a instructions |
| SSSE3 | Conroe SSSE3 functions |
| STIBP | Single Thread Indirect Branch Predictors |
| STIBP_ALWAYSON | AMD: Single Thread Indirect Branch Prediction Mode has Enhanced Performance and may be left Always On |
| STOSB_SHORT | Fast short STOSB |
| SUCCOR | Software uncorrectable error containment and recovery capability. |
| SVM | AMD Secure Virtual Machine |
| SVMDA | Indicates support for the SVM decode assists. |
| SVMFBASID | SVM, Indicates that TLB flush events, including CR3 writes and CR4.PGE toggles, flush only the current ASID's TLB entries. Also indicates support for the extended VMCBTLB_Control |
| SVML | AMD SVM lock. Indicates support for SVM-Lock. |
| SVMNP | AMD SVM nested paging |
| SVMPF | SVM pause intercept filter. Indicates support for the pause intercept filter |
| SVMPFT | SVM PAUSE filter threshold. Indicates support for the PAUSE filter cycle count threshold |
| SYSCALL | System-Call Extension (SCE): SYSCALL and SYSRET instructions. |
| SYSEE | SYSENTER and SYSEXIT instructions |
| TBM | AMD Trailing Bit Manipulation |
| TDX_GUEST | Intel Trust Domain Extensions Guest |
| TLB_FLUSH_NESTED | AMD: Flushing includes all the nested translations for guest translations |
| TME | Intel Total Memory Encryption. The following MSRs are supported: IA32_TME_CAPABILITY, IA32_TME_ACTIVATE, IA32_TME_EXCLUDE_MASK, and IA32_TME_EXCLUDE_BASE. |
| TOPEXT | TopologyExtensions: topology extensions support. Indicates support for CPUID Fn8000_001D_EAX_x[N:0]-CPUID Fn8000_001E_EDX. |
| TSA_L1_NO | AMD only: Not vulnerable to TSA-L1 |
| TSA_SQ_NO | AMD only: Not vulnerable to TSA-SQ |
| TSA_VERW_CLEAR | AMD: If set, the memory form of the VERW instruction may be used to help mitigate TSA |
| TSCRATEMSR | MSR based TSC rate control. Indicates support for MSR TSC ratio MSRC000_0104 |
| TSXLDTRK | Intel TSX Suspend Load Address Tracking |
| VAES | Vector AES. AVX(512) versions requires additional checks. |
| VMCBCLEAN | VMCB clean bits. Indicates support for VMCB clean bits. |
| VMPL | AMD VM Permission Levels supported |
| VMSA_REGPROT | AMD VMSA Register Protection supported |
| VMX | Virtual Machine Extensions |
| VPCLMULQDQ | Carry-Less Multiplication Quadword. Requires AVX for 3 register versions. |
| VTE | AMD Virtual Transparent Encryption supported |
| WAITPKG | TPAUSE, UMONITOR, UMWAIT |
| WBNOINVD | Write Back and Do Not Invalidate Cache |
| WRMSRNS | Non-Serializing Write to Model Specific Register |
| X87 | FPU |
| XGETBV1 | Supports XGETBV with ECX = 1 |
| XOP | Bulldozer XOP functions |
| XSAVE | XSAVE, XRESTOR, XSETBV, XGETBV |
| XSAVEC | Supports XSAVEC and the compacted form of XRSTOR. |
| XSAVEOPT | XSAVEOPT available |
| XSAVES | Supports XSAVES/XRSTORS and IA32_XSS |
# ARM features:
| Feature Flag | Description |
|--------------|------------------------------------------------------------------|
| AESARM | AES instructions |
| ARMCPUID | Some CPU ID registers readable at user-level |
| ASIMD | Advanced SIMD |
| ASIMDDP | SIMD Dot Product |
| ASIMDHP | Advanced SIMD half-precision floating point |
| ASIMDRDM | Rounding Double Multiply Accumulate/Subtract (SQRDMLAH/SQRDMLSH) |
| ATOMICS | Large System Extensions (LSE) |
| CRC32 | CRC32/CRC32C instructions |
| DCPOP | Data cache clean to Point of Persistence (DC CVAP) |
| EVTSTRM | Generic timer |
| FCMA | Floatin point complex number addition and multiplication |
| FHM | FMLAL and FMLSL instructions |
| FP | Single-precision and double-precision floating point |
| FPHP | Half-precision floating point |
| GPA | Generic Pointer Authentication |
| JSCVT | Javascript-style double->int convert (FJCVTZS) |
| LRCPC | Weaker release consistency (LDAPR, etc) |
| PMULL | Polynomial Multiply instructions (PMULL/PMULL2) |
| RNDR | Random Number instructions |
| TLB | Outer Shareable and TLB range maintenance instructions |
| TS | Flag manipulation instructions |
| SHA1 | SHA-1 instructions (SHA1C, etc) |
| SHA2 | SHA-2 instructions (SHA256H, etc) |
| SHA3 | SHA-3 instructions (EOR3, RAXI, XAR, BCAX) |
| SHA512 | SHA512 instructions |
| SM3 | SM3 instructions |
| SM4 | SM4 instructions |
| SVE | Scalable Vector Extension |
# license
This code is published under an MIT license. See LICENSE file for more information.
-1679
View File
File diff suppressed because it is too large Load Diff
-47
View File
@@ -1,47 +0,0 @@
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
//+build 386,!gccgo,!noasm,!appengine
// func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32)
TEXT ·asmCpuid(SB), 7, $0
XORL CX, CX
MOVL op+0(FP), AX
CPUID
MOVL AX, eax+4(FP)
MOVL BX, ebx+8(FP)
MOVL CX, ecx+12(FP)
MOVL DX, edx+16(FP)
RET
// func asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32)
TEXT ·asmCpuidex(SB), 7, $0
MOVL op+0(FP), AX
MOVL op2+4(FP), CX
CPUID
MOVL AX, eax+8(FP)
MOVL BX, ebx+12(FP)
MOVL CX, ecx+16(FP)
MOVL DX, edx+20(FP)
RET
// func xgetbv(index uint32) (eax, edx uint32)
TEXT ·asmXgetbv(SB), 7, $0
MOVL index+0(FP), CX
BYTE $0x0f; BYTE $0x01; BYTE $0xd0 // XGETBV
MOVL AX, eax+4(FP)
MOVL DX, edx+8(FP)
RET
// func asmRdtscpAsm() (eax, ebx, ecx, edx uint32)
TEXT ·asmRdtscpAsm(SB), 7, $0
BYTE $0x0F; BYTE $0x01; BYTE $0xF9 // RDTSCP
MOVL AX, eax+0(FP)
MOVL BX, ebx+4(FP)
MOVL CX, ecx+8(FP)
MOVL DX, edx+12(FP)
RET
// func asmDarwinHasAVX512() bool
TEXT ·asmDarwinHasAVX512(SB), 7, $0
MOVL $0, eax+0(FP)
RET
-72
View File
@@ -1,72 +0,0 @@
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
//+build amd64,!gccgo,!noasm,!appengine
// func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32)
TEXT ·asmCpuid(SB), 7, $0
XORQ CX, CX
MOVL op+0(FP), AX
CPUID
MOVL AX, eax+8(FP)
MOVL BX, ebx+12(FP)
MOVL CX, ecx+16(FP)
MOVL DX, edx+20(FP)
RET
// func asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32)
TEXT ·asmCpuidex(SB), 7, $0
MOVL op+0(FP), AX
MOVL op2+4(FP), CX
CPUID
MOVL AX, eax+8(FP)
MOVL BX, ebx+12(FP)
MOVL CX, ecx+16(FP)
MOVL DX, edx+20(FP)
RET
// func asmXgetbv(index uint32) (eax, edx uint32)
TEXT ·asmXgetbv(SB), 7, $0
MOVL index+0(FP), CX
BYTE $0x0f; BYTE $0x01; BYTE $0xd0 // XGETBV
MOVL AX, eax+8(FP)
MOVL DX, edx+12(FP)
RET
// func asmRdtscpAsm() (eax, ebx, ecx, edx uint32)
TEXT ·asmRdtscpAsm(SB), 7, $0
BYTE $0x0F; BYTE $0x01; BYTE $0xF9 // RDTSCP
MOVL AX, eax+0(FP)
MOVL BX, ebx+4(FP)
MOVL CX, ecx+8(FP)
MOVL DX, edx+12(FP)
RET
// From https://go-review.googlesource.com/c/sys/+/285572/
// func asmDarwinHasAVX512() bool
TEXT ·asmDarwinHasAVX512(SB), 7, $0-1
MOVB $0, ret+0(FP) // default to false
#ifdef GOOS_darwin // return if not darwin
#ifdef GOARCH_amd64 // return if not amd64
// These values from:
// https://github.com/apple/darwin-xnu/blob/xnu-4570.1.46/osfmk/i386/cpu_capabilities.h
#define commpage64_base_address 0x00007fffffe00000
#define commpage64_cpu_capabilities64 (commpage64_base_address+0x010)
#define commpage64_version (commpage64_base_address+0x01E)
#define hasAVX512F 0x0000004000000000
MOVQ $commpage64_version, BX
MOVW (BX), AX
CMPW AX, $13 // versions < 13 do not support AVX512
JL no_avx512
MOVQ $commpage64_cpu_capabilities64, BX
MOVQ (BX), AX
MOVQ $hasAVX512F, CX
ANDQ CX, AX
JZ no_avx512
MOVB $1, ret+0(FP)
no_avx512:
#endif
#endif
RET
-36
View File
@@ -1,36 +0,0 @@
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
//+build arm64,!gccgo,!noasm,!appengine
// See https://www.kernel.org/doc/Documentation/arm64/cpu-feature-registers.txt
// func getMidr
TEXT ·getMidr(SB), 7, $0
WORD $0xd5380000 // mrs x0, midr_el1 /* Main ID Register */
MOVD R0, midr+0(FP)
RET
// func getProcFeatures
TEXT ·getProcFeatures(SB), 7, $0
WORD $0xd5380400 // mrs x0, id_aa64pfr0_el1 /* Processor Feature Register 0 */
MOVD R0, procFeatures+0(FP)
RET
// func getInstAttributes
TEXT ·getInstAttributes(SB), 7, $0
WORD $0xd5380600 // mrs x0, id_aa64isar0_el1 /* Instruction Set Attribute Register 0 */
WORD $0xd5380621 // mrs x1, id_aa64isar1_el1 /* Instruction Set Attribute Register 1 */
MOVD R0, instAttrReg0+0(FP)
MOVD R1, instAttrReg1+8(FP)
RET
TEXT ·getVectorLength(SB), 7, $0
WORD $0xd2800002 // mov x2, #0
WORD $0x04225022 // addvl x2, x2, #1
WORD $0xd37df042 // lsl x2, x2, #3
WORD $0xd2800003 // mov x3, #0
WORD $0x04635023 // addpl x3, x3, #1
WORD $0xd37df063 // lsl x3, x3, #3
MOVD R2, vl+0(FP)
MOVD R3, pl+8(FP)
RET
-250
View File
@@ -1,250 +0,0 @@
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
//go:build arm64 && !gccgo && !noasm && !appengine
// +build arm64,!gccgo,!noasm,!appengine
package cpuid
import "runtime"
func getMidr() (midr uint64)
func getProcFeatures() (procFeatures uint64)
func getInstAttributes() (instAttrReg0, instAttrReg1 uint64)
func getVectorLength() (vl, pl uint64)
func initCPU() {
cpuid = func(uint32) (a, b, c, d uint32) { return 0, 0, 0, 0 }
cpuidex = func(x, y uint32) (a, b, c, d uint32) { return 0, 0, 0, 0 }
xgetbv = func(uint32) (a, b uint32) { return 0, 0 }
rdtscpAsm = func() (a, b, c, d uint32) { return 0, 0, 0, 0 }
}
func addInfo(c *CPUInfo, safe bool) {
// Seems to be safe to assume on ARM64
c.CacheLine = 64
detectOS(c)
// ARM64 disabled since it may crash if interrupt is not intercepted by OS.
if safe && !c.Has(ARMCPUID) && runtime.GOOS != "freebsd" {
return
}
midr := getMidr()
// MIDR_EL1 - Main ID Register
// https://developer.arm.com/docs/ddi0595/h/aarch64-system-registers/midr_el1
// x--------------------------------------------------x
// | Name | bits | visible |
// |--------------------------------------------------|
// | Implementer | [31-24] | y |
// |--------------------------------------------------|
// | Variant | [23-20] | y |
// |--------------------------------------------------|
// | Architecture | [19-16] | y |
// |--------------------------------------------------|
// | PartNum | [15-4] | y |
// |--------------------------------------------------|
// | Revision | [3-0] | y |
// x--------------------------------------------------x
switch (midr >> 24) & 0xff {
case 0xC0:
c.VendorString = "Ampere Computing"
c.VendorID = Ampere
case 0x41:
c.VendorString = "Arm Limited"
c.VendorID = ARM
case 0x42:
c.VendorString = "Broadcom Corporation"
c.VendorID = Broadcom
case 0x43:
c.VendorString = "Cavium Inc"
c.VendorID = Cavium
case 0x44:
c.VendorString = "Digital Equipment Corporation"
c.VendorID = DEC
case 0x46:
c.VendorString = "Fujitsu Ltd"
c.VendorID = Fujitsu
case 0x49:
c.VendorString = "Infineon Technologies AG"
c.VendorID = Infineon
case 0x4D:
c.VendorString = "Motorola or Freescale Semiconductor Inc"
c.VendorID = Motorola
case 0x4E:
c.VendorString = "NVIDIA Corporation"
c.VendorID = NVIDIA
case 0x50:
c.VendorString = "Applied Micro Circuits Corporation"
c.VendorID = AMCC
case 0x51:
c.VendorString = "Qualcomm Inc"
c.VendorID = Qualcomm
case 0x56:
c.VendorString = "Marvell International Ltd"
c.VendorID = Marvell
case 0x69:
c.VendorString = "Intel Corporation"
c.VendorID = Intel
}
// Lower 4 bits: Architecture
// Architecture Meaning
// 0b0001 Armv4.
// 0b0010 Armv4T.
// 0b0011 Armv5 (obsolete).
// 0b0100 Armv5T.
// 0b0101 Armv5TE.
// 0b0110 Armv5TEJ.
// 0b0111 Armv6.
// 0b1111 Architectural features are individually identified in the ID_* registers, see 'ID registers'.
// Upper 4 bit: Variant
// An IMPLEMENTATION DEFINED variant number.
// Typically, this field is used to distinguish between different product variants, or major revisions of a product.
c.Family = int(midr>>16) & 0xff
// PartNum, bits [15:4]
// An IMPLEMENTATION DEFINED primary part number for the device.
// On processors implemented by Arm, if the top four bits of the primary
// part number are 0x0 or 0x7, the variant and architecture are encoded differently.
// Revision, bits [3:0]
// An IMPLEMENTATION DEFINED revision number for the device.
c.Model = int(midr) & 0xffff
procFeatures := getProcFeatures()
// ID_AA64PFR0_EL1 - Processor Feature Register 0
// x--------------------------------------------------x
// | Name | bits | visible |
// |--------------------------------------------------|
// | DIT | [51-48] | y |
// |--------------------------------------------------|
// | SVE | [35-32] | y |
// |--------------------------------------------------|
// | GIC | [27-24] | n |
// |--------------------------------------------------|
// | AdvSIMD | [23-20] | y |
// |--------------------------------------------------|
// | FP | [19-16] | y |
// |--------------------------------------------------|
// | EL3 | [15-12] | n |
// |--------------------------------------------------|
// | EL2 | [11-8] | n |
// |--------------------------------------------------|
// | EL1 | [7-4] | n |
// |--------------------------------------------------|
// | EL0 | [3-0] | n |
// x--------------------------------------------------x
var f flagSet
// if procFeatures&(0xf<<48) != 0 {
// fmt.Println("DIT")
// }
f.setIf(procFeatures&(0xf<<32) != 0, SVE)
if procFeatures&(0xf<<20) != 15<<20 {
f.set(ASIMD)
// https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64pfr0_el1
// 0b0001 --> As for 0b0000, and also includes support for half-precision floating-point arithmetic.
f.setIf(procFeatures&(0xf<<20) == 1<<20, FPHP, ASIMDHP)
}
f.setIf(procFeatures&(0xf<<16) != 0, FP)
instAttrReg0, instAttrReg1 := getInstAttributes()
// https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64isar0_el1
//
// ID_AA64ISAR0_EL1 - Instruction Set Attribute Register 0
// x--------------------------------------------------x
// | Name | bits | visible |
// |--------------------------------------------------|
// | RNDR | [63-60] | y |
// |--------------------------------------------------|
// | TLB | [59-56] | y |
// |--------------------------------------------------|
// | TS | [55-52] | y |
// |--------------------------------------------------|
// | FHM | [51-48] | y |
// |--------------------------------------------------|
// | DP | [47-44] | y |
// |--------------------------------------------------|
// | SM4 | [43-40] | y |
// |--------------------------------------------------|
// | SM3 | [39-36] | y |
// |--------------------------------------------------|
// | SHA3 | [35-32] | y |
// |--------------------------------------------------|
// | RDM | [31-28] | y |
// |--------------------------------------------------|
// | ATOMICS | [23-20] | y |
// |--------------------------------------------------|
// | CRC32 | [19-16] | y |
// |--------------------------------------------------|
// | SHA2 | [15-12] | y |
// |--------------------------------------------------|
// | SHA1 | [11-8] | y |
// |--------------------------------------------------|
// | AES | [7-4] | y |
// x--------------------------------------------------x
f.setIf(instAttrReg0&(0xf<<60) != 0, RNDR)
f.setIf(instAttrReg0&(0xf<<56) != 0, TLB)
f.setIf(instAttrReg0&(0xf<<52) != 0, TS)
f.setIf(instAttrReg0&(0xf<<48) != 0, FHM)
f.setIf(instAttrReg0&(0xf<<44) != 0, ASIMDDP)
f.setIf(instAttrReg0&(0xf<<40) != 0, SM4)
f.setIf(instAttrReg0&(0xf<<36) != 0, SM3)
f.setIf(instAttrReg0&(0xf<<32) != 0, SHA3)
f.setIf(instAttrReg0&(0xf<<28) != 0, ASIMDRDM)
f.setIf(instAttrReg0&(0xf<<20) != 0, ATOMICS)
f.setIf(instAttrReg0&(0xf<<16) != 0, CRC32)
f.setIf(instAttrReg0&(0xf<<12) != 0, SHA2)
// https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64isar0_el1
// 0b0010 --> As 0b0001, plus SHA512H, SHA512H2, SHA512SU0, and SHA512SU1 instructions implemented.
f.setIf(instAttrReg0&(0xf<<12) == 2<<12, SHA512)
f.setIf(instAttrReg0&(0xf<<8) != 0, SHA1)
f.setIf(instAttrReg0&(0xf<<4) != 0, AESARM)
// https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64isar0_el1
// 0b0010 --> As for 0b0001, plus PMULL/PMULL2 instructions operating on 64-bit data quantities.
f.setIf(instAttrReg0&(0xf<<4) == 2<<4, PMULL)
// https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64isar1_el1
//
// ID_AA64ISAR1_EL1 - Instruction set attribute register 1
// x--------------------------------------------------x
// | Name | bits | visible |
// |--------------------------------------------------|
// | GPI | [31-28] | y |
// |--------------------------------------------------|
// | GPA | [27-24] | y |
// |--------------------------------------------------|
// | LRCPC | [23-20] | y |
// |--------------------------------------------------|
// | FCMA | [19-16] | y |
// |--------------------------------------------------|
// | JSCVT | [15-12] | y |
// |--------------------------------------------------|
// | API | [11-8] | y |
// |--------------------------------------------------|
// | APA | [7-4] | y |
// |--------------------------------------------------|
// | DPB | [3-0] | y |
// x--------------------------------------------------x
// if instAttrReg1&(0xf<<28) != 0 {
// fmt.Println("GPI")
// }
f.setIf(instAttrReg1&(0xf<<28) != 24, GPA)
f.setIf(instAttrReg1&(0xf<<20) != 0, LRCPC)
f.setIf(instAttrReg1&(0xf<<16) != 0, FCMA)
f.setIf(instAttrReg1&(0xf<<12) != 0, JSCVT)
// if instAttrReg1&(0xf<<8) != 0 {
// fmt.Println("API")
// }
// if instAttrReg1&(0xf<<4) != 0 {
// fmt.Println("APA")
// }
f.setIf(instAttrReg1&(0xf<<0) != 0, DCPOP)
// Store
c.featureSet.or(f)
}
-17
View File
@@ -1,17 +0,0 @@
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
//go:build (!amd64 && !386 && !arm64) || gccgo || noasm || appengine
// +build !amd64,!386,!arm64 gccgo noasm appengine
package cpuid
func initCPU() {
cpuid = func(uint32) (a, b, c, d uint32) { return 0, 0, 0, 0 }
cpuidex = func(x, y uint32) (a, b, c, d uint32) { return 0, 0, 0, 0 }
xgetbv = func(uint32) (a, b uint32) { return 0, 0 }
rdtscpAsm = func() (a, b, c, d uint32) { return 0, 0, 0, 0 }
}
func addInfo(info *CPUInfo, safe bool) {}
func getVectorLength() (vl, pl uint64) { return 0, 0 }
-45
View File
@@ -1,45 +0,0 @@
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
//go:build (386 && !gccgo && !noasm && !appengine) || (amd64 && !gccgo && !noasm && !appengine)
// +build 386,!gccgo,!noasm,!appengine amd64,!gccgo,!noasm,!appengine
package cpuid
func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32)
func asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32)
func asmXgetbv(index uint32) (eax, edx uint32)
func asmRdtscpAsm() (eax, ebx, ecx, edx uint32)
func asmDarwinHasAVX512() bool
func initCPU() {
cpuid = asmCpuid
cpuidex = asmCpuidex
xgetbv = asmXgetbv
rdtscpAsm = asmRdtscpAsm
darwinHasAVX512 = asmDarwinHasAVX512
}
func addInfo(c *CPUInfo, safe bool) {
c.maxFunc = maxFunctionID()
c.maxExFunc = maxExtendedFunction()
c.BrandName = brandName()
c.CacheLine = cacheLine()
c.Family, c.Model, c.Stepping = familyModel()
c.featureSet = support()
c.SGX = hasSGX(c.featureSet.inSet(SGX), c.featureSet.inSet(SGXLC))
c.AMDMemEncryption = hasAMDMemEncryption(c.featureSet.inSet(SME) || c.featureSet.inSet(SEV))
c.ThreadsPerCore = threadsPerCore()
c.LogicalCores = logicalCores()
c.PhysicalCores = physicalCores()
c.VendorID, c.VendorString = vendorID()
c.HypervisorVendorID, c.HypervisorVendorString = hypervisorVendorID()
c.AVX10Level = c.supportAVX10()
c.cacheSize()
c.frequencies()
if c.maxFunc >= 0x0A {
eax, ebx, _, edx := cpuid(0x0A)
c.PMU = parseLeaf0AH(c, eax, ebx, edx)
}
}
func getVectorLength() (vl, pl uint64) { return 0, 0 }
-308
View File
@@ -1,308 +0,0 @@
// Code generated by "stringer -type=FeatureID,Vendor"; DO NOT EDIT.
package cpuid
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[ADX-1]
_ = x[AESNI-2]
_ = x[AMD3DNOW-3]
_ = x[AMD3DNOWEXT-4]
_ = x[AMXBF16-5]
_ = x[AMXFP16-6]
_ = x[AMXINT8-7]
_ = x[AMXFP8-8]
_ = x[AMXTILE-9]
_ = x[AMXTF32-10]
_ = x[AMXCOMPLEX-11]
_ = x[AMXTRANSPOSE-12]
_ = x[APX_F-13]
_ = x[AVX-14]
_ = x[AVX10-15]
_ = x[AVX10_128-16]
_ = x[AVX10_256-17]
_ = x[AVX10_512-18]
_ = x[AVX2-19]
_ = x[AVX512BF16-20]
_ = x[AVX512BITALG-21]
_ = x[AVX512BW-22]
_ = x[AVX512CD-23]
_ = x[AVX512DQ-24]
_ = x[AVX512ER-25]
_ = x[AVX512F-26]
_ = x[AVX512FP16-27]
_ = x[AVX512IFMA-28]
_ = x[AVX512PF-29]
_ = x[AVX512VBMI-30]
_ = x[AVX512VBMI2-31]
_ = x[AVX512VL-32]
_ = x[AVX512VNNI-33]
_ = x[AVX512VP2INTERSECT-34]
_ = x[AVX512VPOPCNTDQ-35]
_ = x[AVXIFMA-36]
_ = x[AVXNECONVERT-37]
_ = x[AVXSLOW-38]
_ = x[AVXVNNI-39]
_ = x[AVXVNNIINT8-40]
_ = x[AVXVNNIINT16-41]
_ = x[BHI_CTRL-42]
_ = x[BMI1-43]
_ = x[BMI2-44]
_ = x[CETIBT-45]
_ = x[CETSS-46]
_ = x[CLDEMOTE-47]
_ = x[CLMUL-48]
_ = x[CLZERO-49]
_ = x[CMOV-50]
_ = x[CMPCCXADD-51]
_ = x[CMPSB_SCADBS_SHORT-52]
_ = x[CMPXCHG8-53]
_ = x[CPBOOST-54]
_ = x[CPPC-55]
_ = x[CX16-56]
_ = x[EFER_LMSLE_UNS-57]
_ = x[ENQCMD-58]
_ = x[ERMS-59]
_ = x[F16C-60]
_ = x[FLUSH_L1D-61]
_ = x[FMA3-62]
_ = x[FMA4-63]
_ = x[FP128-64]
_ = x[FP256-65]
_ = x[FSRM-66]
_ = x[FXSR-67]
_ = x[FXSROPT-68]
_ = x[GFNI-69]
_ = x[HLE-70]
_ = x[HRESET-71]
_ = x[HTT-72]
_ = x[HWA-73]
_ = x[HYBRID_CPU-74]
_ = x[HYPERVISOR-75]
_ = x[IA32_ARCH_CAP-76]
_ = x[IA32_CORE_CAP-77]
_ = x[IBPB-78]
_ = x[IBPB_BRTYPE-79]
_ = x[IBRS-80]
_ = x[IBRS_PREFERRED-81]
_ = x[IBRS_PROVIDES_SMP-82]
_ = x[IBS-83]
_ = x[IBSBRNTRGT-84]
_ = x[IBSFETCHSAM-85]
_ = x[IBSFFV-86]
_ = x[IBSOPCNT-87]
_ = x[IBSOPCNTEXT-88]
_ = x[IBSOPSAM-89]
_ = x[IBSRDWROPCNT-90]
_ = x[IBSRIPINVALIDCHK-91]
_ = x[IBS_FETCH_CTLX-92]
_ = x[IBS_OPDATA4-93]
_ = x[IBS_OPFUSE-94]
_ = x[IBS_PREVENTHOST-95]
_ = x[IBS_ZEN4-96]
_ = x[IDPRED_CTRL-97]
_ = x[INT_WBINVD-98]
_ = x[INVLPGB-99]
_ = x[KEYLOCKER-100]
_ = x[KEYLOCKERW-101]
_ = x[LAHF-102]
_ = x[LAM-103]
_ = x[LBRVIRT-104]
_ = x[LZCNT-105]
_ = x[MCAOVERFLOW-106]
_ = x[MCDT_NO-107]
_ = x[MCOMMIT-108]
_ = x[MD_CLEAR-109]
_ = x[MMX-110]
_ = x[MMXEXT-111]
_ = x[MOVBE-112]
_ = x[MOVDIR64B-113]
_ = x[MOVDIRI-114]
_ = x[MOVSB_ZL-115]
_ = x[MOVU-116]
_ = x[MPX-117]
_ = x[MSRIRC-118]
_ = x[MSRLIST-119]
_ = x[MSR_PAGEFLUSH-120]
_ = x[NRIPS-121]
_ = x[NX-122]
_ = x[OSXSAVE-123]
_ = x[PCONFIG-124]
_ = x[POPCNT-125]
_ = x[PPIN-126]
_ = x[PREFETCHI-127]
_ = x[PSFD-128]
_ = x[RDPRU-129]
_ = x[RDRAND-130]
_ = x[RDSEED-131]
_ = x[RDTSCP-132]
_ = x[RRSBA_CTRL-133]
_ = x[RTM-134]
_ = x[RTM_ALWAYS_ABORT-135]
_ = x[SBPB-136]
_ = x[SERIALIZE-137]
_ = x[SEV-138]
_ = x[SEV_64BIT-139]
_ = x[SEV_ALTERNATIVE-140]
_ = x[SEV_DEBUGSWAP-141]
_ = x[SEV_ES-142]
_ = x[SEV_RESTRICTED-143]
_ = x[SEV_SNP-144]
_ = x[SGX-145]
_ = x[SGXLC-146]
_ = x[SGXPQC-147]
_ = x[SHA-148]
_ = x[SME-149]
_ = x[SME_COHERENT-150]
_ = x[SM3_X86-151]
_ = x[SM4_X86-152]
_ = x[SPEC_CTRL_SSBD-153]
_ = x[SRBDS_CTRL-154]
_ = x[SRSO_MSR_FIX-155]
_ = x[SRSO_NO-156]
_ = x[SRSO_USER_KERNEL_NO-157]
_ = x[SSE-158]
_ = x[SSE2-159]
_ = x[SSE3-160]
_ = x[SSE4-161]
_ = x[SSE42-162]
_ = x[SSE4A-163]
_ = x[SSSE3-164]
_ = x[STIBP-165]
_ = x[STIBP_ALWAYSON-166]
_ = x[STOSB_SHORT-167]
_ = x[SUCCOR-168]
_ = x[SVM-169]
_ = x[SVMDA-170]
_ = x[SVMFBASID-171]
_ = x[SVML-172]
_ = x[SVMNP-173]
_ = x[SVMPF-174]
_ = x[SVMPFT-175]
_ = x[SYSCALL-176]
_ = x[SYSEE-177]
_ = x[TBM-178]
_ = x[TDX_GUEST-179]
_ = x[TLB_FLUSH_NESTED-180]
_ = x[TME-181]
_ = x[TOPEXT-182]
_ = x[TSA_L1_NO-183]
_ = x[TSA_SQ_NO-184]
_ = x[TSA_VERW_CLEAR-185]
_ = x[TSCRATEMSR-186]
_ = x[TSXLDTRK-187]
_ = x[VAES-188]
_ = x[VMCBCLEAN-189]
_ = x[VMPL-190]
_ = x[VMSA_REGPROT-191]
_ = x[VMX-192]
_ = x[VPCLMULQDQ-193]
_ = x[VTE-194]
_ = x[WAITPKG-195]
_ = x[WBNOINVD-196]
_ = x[WRMSRNS-197]
_ = x[X87-198]
_ = x[XGETBV1-199]
_ = x[XOP-200]
_ = x[XSAVE-201]
_ = x[XSAVEC-202]
_ = x[XSAVEOPT-203]
_ = x[XSAVES-204]
_ = x[AESARM-205]
_ = x[ARMCPUID-206]
_ = x[ASIMD-207]
_ = x[ASIMDDP-208]
_ = x[ASIMDHP-209]
_ = x[ASIMDRDM-210]
_ = x[ATOMICS-211]
_ = x[CRC32-212]
_ = x[DCPOP-213]
_ = x[EVTSTRM-214]
_ = x[FCMA-215]
_ = x[FHM-216]
_ = x[FP-217]
_ = x[FPHP-218]
_ = x[GPA-219]
_ = x[JSCVT-220]
_ = x[LRCPC-221]
_ = x[PMULL-222]
_ = x[RNDR-223]
_ = x[TLB-224]
_ = x[TS-225]
_ = x[SHA1-226]
_ = x[SHA2-227]
_ = x[SHA3-228]
_ = x[SHA512-229]
_ = x[SM3-230]
_ = x[SM4-231]
_ = x[SVE-232]
_ = x[PMU_FIXEDCOUNTER_CYCLES-233]
_ = x[PMU_FIXEDCOUNTER_REFCYCLES-234]
_ = x[PMU_FIXEDCOUNTER_INSTRUCTIONS-235]
_ = x[PMU_FIXEDCOUNTER_TOPDOWN_SLOTS-236]
_ = x[lastID-237]
_ = x[firstID-0]
}
const _FeatureID_name = "firstIDADXAESNIAMD3DNOWAMD3DNOWEXTAMXBF16AMXFP16AMXINT8AMXFP8AMXTILEAMXTF32AMXCOMPLEXAMXTRANSPOSEAPX_FAVXAVX10AVX10_128AVX10_256AVX10_512AVX2AVX512BF16AVX512BITALGAVX512BWAVX512CDAVX512DQAVX512ERAVX512FAVX512FP16AVX512IFMAAVX512PFAVX512VBMIAVX512VBMI2AVX512VLAVX512VNNIAVX512VP2INTERSECTAVX512VPOPCNTDQAVXIFMAAVXNECONVERTAVXSLOWAVXVNNIAVXVNNIINT8AVXVNNIINT16BHI_CTRLBMI1BMI2CETIBTCETSSCLDEMOTECLMULCLZEROCMOVCMPCCXADDCMPSB_SCADBS_SHORTCMPXCHG8CPBOOSTCPPCCX16EFER_LMSLE_UNSENQCMDERMSF16CFLUSH_L1DFMA3FMA4FP128FP256FSRMFXSRFXSROPTGFNIHLEHRESETHTTHWAHYBRID_CPUHYPERVISORIA32_ARCH_CAPIA32_CORE_CAPIBPBIBPB_BRTYPEIBRSIBRS_PREFERREDIBRS_PROVIDES_SMPIBSIBSBRNTRGTIBSFETCHSAMIBSFFVIBSOPCNTIBSOPCNTEXTIBSOPSAMIBSRDWROPCNTIBSRIPINVALIDCHKIBS_FETCH_CTLXIBS_OPDATA4IBS_OPFUSEIBS_PREVENTHOSTIBS_ZEN4IDPRED_CTRLINT_WBINVDINVLPGBKEYLOCKERKEYLOCKERWLAHFLAMLBRVIRTLZCNTMCAOVERFLOWMCDT_NOMCOMMITMD_CLEARMMXMMXEXTMOVBEMOVDIR64BMOVDIRIMOVSB_ZLMOVUMPXMSRIRCMSRLISTMSR_PAGEFLUSHNRIPSNXOSXSAVEPCONFIGPOPCNTPPINPREFETCHIPSFDRDPRURDRANDRDSEEDRDTSCPRRSBA_CTRLRTMRTM_ALWAYS_ABORTSBPBSERIALIZESEVSEV_64BITSEV_ALTERNATIVESEV_DEBUGSWAPSEV_ESSEV_RESTRICTEDSEV_SNPSGXSGXLCSGXPQCSHASMESME_COHERENTSM3_X86SM4_X86SPEC_CTRL_SSBDSRBDS_CTRLSRSO_MSR_FIXSRSO_NOSRSO_USER_KERNEL_NOSSESSE2SSE3SSE4SSE42SSE4ASSSE3STIBPSTIBP_ALWAYSONSTOSB_SHORTSUCCORSVMSVMDASVMFBASIDSVMLSVMNPSVMPFSVMPFTSYSCALLSYSEETBMTDX_GUESTTLB_FLUSH_NESTEDTMETOPEXTTSA_L1_NOTSA_SQ_NOTSA_VERW_CLEARTSCRATEMSRTSXLDTRKVAESVMCBCLEANVMPLVMSA_REGPROTVMXVPCLMULQDQVTEWAITPKGWBNOINVDWRMSRNSX87XGETBV1XOPXSAVEXSAVECXSAVEOPTXSAVESAESARMARMCPUIDASIMDASIMDDPASIMDHPASIMDRDMATOMICSCRC32DCPOPEVTSTRMFCMAFHMFPFPHPGPAJSCVTLRCPCPMULLRNDRTLBTSSHA1SHA2SHA3SHA512SM3SM4SVEPMU_FIXEDCOUNTER_CYCLESPMU_FIXEDCOUNTER_REFCYCLESPMU_FIXEDCOUNTER_INSTRUCTIONSPMU_FIXEDCOUNTER_TOPDOWN_SLOTSlastID"
var _FeatureID_index = [...]uint16{0, 7, 10, 15, 23, 34, 41, 48, 55, 61, 68, 75, 85, 97, 102, 105, 110, 119, 128, 137, 141, 151, 163, 171, 179, 187, 195, 202, 212, 222, 230, 240, 251, 259, 269, 287, 302, 309, 321, 328, 335, 346, 358, 366, 370, 374, 380, 385, 393, 398, 404, 408, 417, 435, 443, 450, 454, 458, 472, 478, 482, 486, 495, 499, 503, 508, 513, 517, 521, 528, 532, 535, 541, 544, 547, 557, 567, 580, 593, 597, 608, 612, 626, 643, 646, 656, 667, 673, 681, 692, 700, 712, 728, 742, 753, 763, 778, 786, 797, 807, 814, 823, 833, 837, 840, 847, 852, 863, 870, 877, 885, 888, 894, 899, 908, 915, 923, 927, 930, 936, 943, 956, 961, 963, 970, 977, 983, 987, 996, 1000, 1005, 1011, 1017, 1023, 1033, 1036, 1052, 1056, 1065, 1068, 1077, 1092, 1105, 1111, 1125, 1132, 1135, 1140, 1146, 1149, 1152, 1164, 1171, 1178, 1192, 1202, 1214, 1221, 1240, 1243, 1247, 1251, 1255, 1260, 1265, 1270, 1275, 1289, 1300, 1306, 1309, 1314, 1323, 1327, 1332, 1337, 1343, 1350, 1355, 1358, 1367, 1383, 1386, 1392, 1401, 1410, 1424, 1434, 1442, 1446, 1455, 1459, 1471, 1474, 1484, 1487, 1494, 1502, 1509, 1512, 1519, 1522, 1527, 1533, 1541, 1547, 1553, 1561, 1566, 1573, 1580, 1588, 1595, 1600, 1605, 1612, 1616, 1619, 1621, 1625, 1628, 1633, 1638, 1643, 1647, 1650, 1652, 1656, 1660, 1664, 1670, 1673, 1676, 1679, 1702, 1728, 1757, 1787, 1793}
func (i FeatureID) String() string {
if i < 0 || i >= FeatureID(len(_FeatureID_index)-1) {
return "FeatureID(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _FeatureID_name[_FeatureID_index[i]:_FeatureID_index[i+1]]
}
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[VendorUnknown-0]
_ = x[Intel-1]
_ = x[AMD-2]
_ = x[VIA-3]
_ = x[Transmeta-4]
_ = x[NSC-5]
_ = x[KVM-6]
_ = x[MSVM-7]
_ = x[VMware-8]
_ = x[XenHVM-9]
_ = x[Bhyve-10]
_ = x[Hygon-11]
_ = x[SiS-12]
_ = x[RDC-13]
_ = x[Ampere-14]
_ = x[ARM-15]
_ = x[Broadcom-16]
_ = x[Cavium-17]
_ = x[DEC-18]
_ = x[Fujitsu-19]
_ = x[Infineon-20]
_ = x[Motorola-21]
_ = x[NVIDIA-22]
_ = x[AMCC-23]
_ = x[Qualcomm-24]
_ = x[Marvell-25]
_ = x[QEMU-26]
_ = x[QNX-27]
_ = x[ACRN-28]
_ = x[SRE-29]
_ = x[Apple-30]
_ = x[lastVendor-31]
}
const _Vendor_name = "VendorUnknownIntelAMDVIATransmetaNSCKVMMSVMVMwareXenHVMBhyveHygonSiSRDCAmpereARMBroadcomCaviumDECFujitsuInfineonMotorolaNVIDIAAMCCQualcommMarvellQEMUQNXACRNSREApplelastVendor"
var _Vendor_index = [...]uint8{0, 13, 18, 21, 24, 33, 36, 39, 43, 49, 55, 60, 65, 68, 71, 77, 80, 88, 94, 97, 104, 112, 120, 126, 130, 138, 145, 149, 152, 156, 159, 164, 174}
func (i Vendor) String() string {
if i < 0 || i >= Vendor(len(_Vendor_index)-1) {
return "Vendor(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _Vendor_name[_Vendor_index[i]:_Vendor_index[i+1]]
}
-129
View File
@@ -1,129 +0,0 @@
// Copyright (c) 2020 Klaus Post, released under MIT License. See LICENSE file.
package cpuid
import (
"runtime"
"strings"
"golang.org/x/sys/unix"
)
func detectOS(c *CPUInfo) bool {
if runtime.GOOS != "ios" {
tryToFillCPUInfoFomSysctl(c)
}
// There are no hw.optional sysctl values for the below features on Mac OS 11.0
// to detect their supported state dynamically. Assume the CPU features that
// Apple Silicon M1 supports to be available as a minimal set of features
// to all Go programs running on darwin/arm64.
// TODO: Add more if we know them.
c.featureSet.setIf(runtime.GOOS != "ios", AESARM, PMULL, SHA1, SHA2)
return true
}
func sysctlGetBool(name string) bool {
value, err := unix.SysctlUint32(name)
if err != nil {
return false
}
return value != 0
}
func sysctlGetString(name string) string {
value, err := unix.Sysctl(name)
if err != nil {
return ""
}
return value
}
func sysctlGetInt(unknown int, names ...string) int {
for _, name := range names {
value, err := unix.SysctlUint32(name)
if err != nil {
continue
}
if value != 0 {
return int(value)
}
}
return unknown
}
func sysctlGetInt64(unknown int, names ...string) int {
for _, name := range names {
value64, err := unix.SysctlUint64(name)
if err != nil {
continue
}
if int(value64) != unknown {
return int(value64)
}
}
return unknown
}
func setFeature(c *CPUInfo, feature FeatureID, aliases ...string) {
for _, alias := range aliases {
set := sysctlGetBool(alias)
c.featureSet.setIf(set, feature)
if set {
break
}
}
}
func tryToFillCPUInfoFomSysctl(c *CPUInfo) {
c.BrandName = sysctlGetString("machdep.cpu.brand_string")
if len(c.BrandName) != 0 {
c.VendorString = strings.Fields(c.BrandName)[0]
}
c.PhysicalCores = sysctlGetInt(runtime.NumCPU(), "hw.physicalcpu")
c.ThreadsPerCore = sysctlGetInt(1, "machdep.cpu.thread_count", "kern.num_threads") /
sysctlGetInt(1, "hw.physicalcpu")
c.LogicalCores = sysctlGetInt(runtime.NumCPU(), "machdep.cpu.core_count")
c.Family = sysctlGetInt(0, "machdep.cpu.family", "hw.cpufamily")
c.Model = sysctlGetInt(0, "machdep.cpu.model")
c.CacheLine = sysctlGetInt64(0, "hw.cachelinesize")
c.Cache.L1I = sysctlGetInt64(-1, "hw.l1icachesize")
c.Cache.L1D = sysctlGetInt64(-1, "hw.l1dcachesize")
c.Cache.L2 = sysctlGetInt64(-1, "hw.l2cachesize")
c.Cache.L3 = sysctlGetInt64(-1, "hw.l3cachesize")
// ARM features:
//
// Note: On some Apple Silicon system, some feats have aliases. See:
// https://developer.apple.com/documentation/kernel/1387446-sysctlbyname/determining_instruction_set_characteristics
// When so, we look at all aliases and consider a feature available when at least one identifier matches.
setFeature(c, AESARM, "hw.optional.arm.FEAT_AES") // AES instructions
setFeature(c, ASIMD, "hw.optional.arm.AdvSIMD", "hw.optional.neon") // Advanced SIMD
setFeature(c, ASIMDDP, "hw.optional.arm.FEAT_DotProd") // SIMD Dot Product
setFeature(c, ASIMDHP, "hw.optional.arm.AdvSIMD_HPFPCvt", "hw.optional.neon_hpfp") // Advanced SIMD half-precision floating point
setFeature(c, ASIMDRDM, "hw.optional.arm.FEAT_RDM") // Rounding Double Multiply Accumulate/Subtract
setFeature(c, ATOMICS, "hw.optional.arm.FEAT_LSE", "hw.optional.armv8_1_atomics") // Large System Extensions (LSE)
setFeature(c, CRC32, "hw.optional.arm.FEAT_CRC32", "hw.optional.armv8_crc32") // CRC32/CRC32C instructions
setFeature(c, DCPOP, "hw.optional.arm.FEAT_DPB") // Data cache clean to Point of Persistence (DC CVAP)
setFeature(c, EVTSTRM, "hw.optional.arm.FEAT_ECV") // Generic timer
setFeature(c, FCMA, "hw.optional.arm.FEAT_FCMA", "hw.optional.armv8_3_compnum") // Floating point complex number addition and multiplication
setFeature(c, FHM, "hw.optional.armv8_2_fhm", "hw.optional.arm.FEAT_FHM") // FMLAL and FMLSL instructions
setFeature(c, FP, "hw.optional.floatingpoint") // Single-precision and double-precision floating point
setFeature(c, FPHP, "hw.optional.arm.FEAT_FP16", "hw.optional.neon_fp16") // Half-precision floating point
setFeature(c, GPA, "hw.optional.arm.FEAT_PAuth") // Generic Pointer Authentication
setFeature(c, JSCVT, "hw.optional.arm.FEAT_JSCVT") // Javascript-style double->int convert (FJCVTZS)
setFeature(c, LRCPC, "hw.optional.arm.FEAT_LRCPC") // Weaker release consistency (LDAPR, etc)
setFeature(c, PMULL, "hw.optional.arm.FEAT_PMULL") // Polynomial Multiply instructions (PMULL/PMULL2)
setFeature(c, RNDR, "hw.optional.arm.FEAT_RNG") // Random Number instructions
setFeature(c, TLB, "hw.optional.arm.FEAT_TLBIOS", "hw.optional.arm.FEAT_TLBIRANGE") // Outer Shareable and TLB range maintenance instructions
setFeature(c, TS, "hw.optional.arm.FEAT_FlagM", "hw.optional.arm.FEAT_FlagM2") // Flag manipulation instructions
setFeature(c, SHA1, "hw.optional.arm.FEAT_SHA1") // SHA-1 instructions (SHA1C, etc)
setFeature(c, SHA2, "hw.optional.arm.FEAT_SHA256") // SHA-2 instructions (SHA256H, etc)
setFeature(c, SHA3, "hw.optional.arm.FEAT_SHA3") // SHA-3 instructions (EOR3, RAXI, XAR, BCAX)
setFeature(c, SHA512, "hw.optional.arm.FEAT_SHA512") // SHA512 instructions
setFeature(c, SM3, "hw.optional.arm.FEAT_SM3") // SM3 instructions
setFeature(c, SM4, "hw.optional.arm.FEAT_SM4") // SM4 instructions
setFeature(c, SVE, "hw.optional.arm.FEAT_SVE") // Scalable Vector Extension
}
-208
View File
@@ -1,208 +0,0 @@
// Copyright (c) 2020 Klaus Post, released under MIT License. See LICENSE file.
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file located
// here https://github.com/golang/sys/blob/master/LICENSE
package cpuid
import (
"encoding/binary"
"io/ioutil"
"runtime"
)
// HWCAP bits.
const (
hwcap_FP = 1 << 0
hwcap_ASIMD = 1 << 1
hwcap_EVTSTRM = 1 << 2
hwcap_AES = 1 << 3
hwcap_PMULL = 1 << 4
hwcap_SHA1 = 1 << 5
hwcap_SHA2 = 1 << 6
hwcap_CRC32 = 1 << 7
hwcap_ATOMICS = 1 << 8
hwcap_FPHP = 1 << 9
hwcap_ASIMDHP = 1 << 10
hwcap_CPUID = 1 << 11
hwcap_ASIMDRDM = 1 << 12
hwcap_JSCVT = 1 << 13
hwcap_FCMA = 1 << 14
hwcap_LRCPC = 1 << 15
hwcap_DCPOP = 1 << 16
hwcap_SHA3 = 1 << 17
hwcap_SM3 = 1 << 18
hwcap_SM4 = 1 << 19
hwcap_ASIMDDP = 1 << 20
hwcap_SHA512 = 1 << 21
hwcap_SVE = 1 << 22
hwcap_ASIMDFHM = 1 << 23
hwcap_DIT = 1 << 24
hwcap_USCAT = 1 << 25
hwcap_ILRCPC = 1 << 26
hwcap_FLAGM = 1 << 27
hwcap_SSBS = 1 << 28
hwcap_SB = 1 << 29
hwcap_PACA = 1 << 30
hwcap_PACG = 1 << 31
hwcap_GCS = 1 << 32
hwcap2_DCPODP = 1 << 0
hwcap2_SVE2 = 1 << 1
hwcap2_SVEAES = 1 << 2
hwcap2_SVEPMULL = 1 << 3
hwcap2_SVEBITPERM = 1 << 4
hwcap2_SVESHA3 = 1 << 5
hwcap2_SVESM4 = 1 << 6
hwcap2_FLAGM2 = 1 << 7
hwcap2_FRINT = 1 << 8
hwcap2_SVEI8MM = 1 << 9
hwcap2_SVEF32MM = 1 << 10
hwcap2_SVEF64MM = 1 << 11
hwcap2_SVEBF16 = 1 << 12
hwcap2_I8MM = 1 << 13
hwcap2_BF16 = 1 << 14
hwcap2_DGH = 1 << 15
hwcap2_RNG = 1 << 16
hwcap2_BTI = 1 << 17
hwcap2_MTE = 1 << 18
hwcap2_ECV = 1 << 19
hwcap2_AFP = 1 << 20
hwcap2_RPRES = 1 << 21
hwcap2_MTE3 = 1 << 22
hwcap2_SME = 1 << 23
hwcap2_SME_I16I64 = 1 << 24
hwcap2_SME_F64F64 = 1 << 25
hwcap2_SME_I8I32 = 1 << 26
hwcap2_SME_F16F32 = 1 << 27
hwcap2_SME_B16F32 = 1 << 28
hwcap2_SME_F32F32 = 1 << 29
hwcap2_SME_FA64 = 1 << 30
hwcap2_WFXT = 1 << 31
hwcap2_EBF16 = 1 << 32
hwcap2_SVE_EBF16 = 1 << 33
hwcap2_CSSC = 1 << 34
hwcap2_RPRFM = 1 << 35
hwcap2_SVE2P1 = 1 << 36
hwcap2_SME2 = 1 << 37
hwcap2_SME2P1 = 1 << 38
hwcap2_SME_I16I32 = 1 << 39
hwcap2_SME_BI32I32 = 1 << 40
hwcap2_SME_B16B16 = 1 << 41
hwcap2_SME_F16F16 = 1 << 42
hwcap2_MOPS = 1 << 43
hwcap2_HBC = 1 << 44
hwcap2_SVE_B16B16 = 1 << 45
hwcap2_LRCPC3 = 1 << 46
hwcap2_LSE128 = 1 << 47
hwcap2_FPMR = 1 << 48
hwcap2_LUT = 1 << 49
hwcap2_FAMINMAX = 1 << 50
hwcap2_F8CVT = 1 << 51
hwcap2_F8FMA = 1 << 52
hwcap2_F8DP4 = 1 << 53
hwcap2_F8DP2 = 1 << 54
hwcap2_F8E4M3 = 1 << 55
hwcap2_F8E5M2 = 1 << 56
hwcap2_SME_LUTV2 = 1 << 57
hwcap2_SME_F8F16 = 1 << 58
hwcap2_SME_F8F32 = 1 << 59
hwcap2_SME_SF8FMA = 1 << 60
hwcap2_SME_SF8DP4 = 1 << 61
hwcap2_SME_SF8DP2 = 1 << 62
hwcap2_POE = 1 << 63
)
func detectOS(c *CPUInfo) bool {
// For now assuming no hyperthreading is reasonable.
c.LogicalCores = runtime.NumCPU()
c.PhysicalCores = c.LogicalCores
c.ThreadsPerCore = 1
if hwcap == 0 {
// We did not get values from the runtime.
// Try reading /proc/self/auxv
// From https://github.com/golang/sys
const (
_AT_HWCAP = 16
_AT_HWCAP2 = 26
uintSize = int(32 << (^uint(0) >> 63))
)
buf, err := ioutil.ReadFile("/proc/self/auxv")
if err != nil {
// e.g. on android /proc/self/auxv is not accessible, so silently
// ignore the error and leave Initialized = false. On some
// architectures (e.g. arm64) doinit() implements a fallback
// readout and will set Initialized = true again.
return false
}
bo := binary.LittleEndian
for len(buf) >= 2*(uintSize/8) {
var tag, val uint
switch uintSize {
case 32:
tag = uint(bo.Uint32(buf[0:]))
val = uint(bo.Uint32(buf[4:]))
buf = buf[8:]
case 64:
tag = uint(bo.Uint64(buf[0:]))
val = uint(bo.Uint64(buf[8:]))
buf = buf[16:]
}
switch tag {
case _AT_HWCAP:
hwcap = val
case _AT_HWCAP2:
// Not used
}
}
if hwcap == 0 {
return false
}
}
// HWCap was populated by the runtime from the auxiliary vector.
// Use HWCap information since reading aarch64 system registers
// is not supported in user space on older linux kernels.
c.featureSet.setIf(isSet(hwcap, hwcap_AES), AESARM)
c.featureSet.setIf(isSet(hwcap, hwcap_ASIMD), ASIMD)
c.featureSet.setIf(isSet(hwcap, hwcap_ASIMDDP), ASIMDDP)
c.featureSet.setIf(isSet(hwcap, hwcap_ASIMDHP), ASIMDHP)
c.featureSet.setIf(isSet(hwcap, hwcap_ASIMDRDM), ASIMDRDM)
c.featureSet.setIf(isSet(hwcap, hwcap_CPUID), ARMCPUID)
c.featureSet.setIf(isSet(hwcap, hwcap_CRC32), CRC32)
c.featureSet.setIf(isSet(hwcap, hwcap_DCPOP), DCPOP)
c.featureSet.setIf(isSet(hwcap, hwcap_EVTSTRM), EVTSTRM)
c.featureSet.setIf(isSet(hwcap, hwcap_FCMA), FCMA)
c.featureSet.setIf(isSet(hwcap, hwcap_ASIMDFHM), FHM)
c.featureSet.setIf(isSet(hwcap, hwcap_FP), FP)
c.featureSet.setIf(isSet(hwcap, hwcap_FPHP), FPHP)
c.featureSet.setIf(isSet(hwcap, hwcap_JSCVT), JSCVT)
c.featureSet.setIf(isSet(hwcap, hwcap_LRCPC), LRCPC)
c.featureSet.setIf(isSet(hwcap, hwcap_PMULL), PMULL)
c.featureSet.setIf(isSet(hwcap, hwcap2_RNG), RNDR)
// c.featureSet.setIf(isSet(hwcap, hwcap_), TLB)
// c.featureSet.setIf(isSet(hwcap, hwcap_), TS)
c.featureSet.setIf(isSet(hwcap, hwcap_SHA1), SHA1)
c.featureSet.setIf(isSet(hwcap, hwcap_SHA2), SHA2)
c.featureSet.setIf(isSet(hwcap, hwcap_SHA3), SHA3)
c.featureSet.setIf(isSet(hwcap, hwcap_SHA512), SHA512)
c.featureSet.setIf(isSet(hwcap, hwcap_SM3), SM3)
c.featureSet.setIf(isSet(hwcap, hwcap_SM4), SM4)
c.featureSet.setIf(isSet(hwcap, hwcap_SVE), SVE)
// The Samsung S9+ kernel reports support for atomics, but not all cores
// actually support them, resulting in SIGILL. See issue #28431.
// TODO(elias.naur): Only disable the optimization on bad chipsets on android.
c.featureSet.setIf(isSet(hwcap, hwcap_ATOMICS) && runtime.GOOS != "android", ATOMICS)
return true
}
func isSet(hwc uint, value uint) bool {
return hwc&value != 0
}
-16
View File
@@ -1,16 +0,0 @@
// Copyright (c) 2020 Klaus Post, released under MIT License. See LICENSE file.
//go:build arm64 && !linux && !darwin
// +build arm64,!linux,!darwin
package cpuid
import "runtime"
func detectOS(c *CPUInfo) bool {
c.PhysicalCores = runtime.NumCPU()
// For now assuming 1 thread per core...
c.ThreadsPerCore = 1
c.LogicalCores = c.PhysicalCores
return false
}
-8
View File
@@ -1,8 +0,0 @@
// Copyright (c) 2021 Klaus Post, released under MIT License. See LICENSE file.
//go:build nounsafe
// +build nounsafe
package cpuid
var hwcap uint
-11
View File
@@ -1,11 +0,0 @@
// Copyright (c) 2021 Klaus Post, released under MIT License. See LICENSE file.
//go:build !nounsafe
// +build !nounsafe
package cpuid
import _ "unsafe" // needed for go:linkname
//go:linkname hwcap internal/cpu.HWCap
var hwcap uint
-15
View File
@@ -1,15 +0,0 @@
#!/bin/sh
set -e
go tool dist list | while IFS=/ read os arch; do
echo "Checking $os/$arch..."
echo " normal"
GOARCH=$arch GOOS=$os go build -o /dev/null .
echo " noasm"
GOARCH=$arch GOOS=$os go build -tags noasm -o /dev/null .
echo " appengine"
GOARCH=$arch GOOS=$os go build -tags appengine -o /dev/null .
echo " noasm,appengine"
GOARCH=$arch GOOS=$os go build -tags 'appengine noasm' -o /dev/null .
done
+1 -1
View File
@@ -1,4 +1,4 @@
FROM golang:1.26@sha256:b900de91b15b2e2953d930ece1d0ecff0a1590ab2006088d20dcf0f56f1e979f
FROM golang:1.27@sha256:e432b43af23a9328d56a7c499be0476810aa344acbcf65fc7c455d4ff5a40602
ENV GOOS=linux
ENV GOARCH=arm
+1 -1
View File
@@ -1,4 +1,4 @@
FROM golang:1.26@sha256:b900de91b15b2e2953d930ece1d0ecff0a1590ab2006088d20dcf0f56f1e979f
FROM golang:1.27@sha256:e432b43af23a9328d56a7c499be0476810aa344acbcf65fc7c455d4ff5a40602
ENV GOOS=linux
ENV GOARCH=arm64
+23 -4
View File
@@ -1,18 +1,35 @@
FUZZ_TIME ?= 1m
FUZZ_FLAGS ?=
# The fuzz targets of each package, along with the tags needed to build them.
FUZZ_TARGETS = ./test/:gofuzz .:sha1cd_asmtest
export CGO_ENABLED := 1
# The hashing tests run a second time with SHA-NI off, so that CPUs with it
# also cover the AVX2 fallback that CPUs without it use.
.PHONY: test
test:
go test -race -timeout 15s ./...
go test -race -timeout 15s -tags sha1cd_asmtest ./...
SHA1CD_TEST_NOSHANI=1 go test -race -timeout 15s -tags sha1cd_asmtest ./test/
.PHONY: bench
bench:
go test -benchmem -run=^$$ -bench ^Benchmark ./...
# go test only fuzzes a single target per invocation, so each one is run in
# turn for FUZZ_TIME.
.PHONY: fuzz
fuzz:
go test -tags gofuzz -fuzz=. -fuzztime=$(FUZZ_TIME) ./test/
@set -e; for entry in $(FUZZ_TARGETS); do \
pkg="$${entry%%:*}"; tags="$${entry##*:}"; \
listed="$$(go test -tags "$$tags" -list '^Fuzz' "$$pkg")"; \
for target in $$(echo "$$listed" | grep '^Fuzz'); do \
echo "fuzzing $$target in $$pkg for $(FUZZ_TIME)"; \
go test $(FUZZ_FLAGS) -tags "$$tags" -run '^$$' -fuzz "^$$target"'$$' \
-fuzztime=$(FUZZ_TIME) "$$pkg"; \
done; \
done
# Cross build project in arm/v7.
build-arm:
@@ -24,9 +41,11 @@ build-arm64:
docker build -t sha1cd-arm64 -f Dockerfile.arm64 .
docker run --rm sha1cd-arm64
# Build with cgo disabled.
# Build with cgo disabled. Vetting every package (not just ./cgo) is what
# catches the cgo and non-cgo builds drifting apart in their exported API.
build-nocgo:
CGO_ENABLED=0 go build ./cgo
CGO_ENABLED=0 go build ./...
CGO_ENABLED=0 go vet ./...
# Run cross-compilation to assure supported architectures.
cross-build: build-arm build-arm64 build-nocgo
+28
View File
@@ -0,0 +1,28 @@
// Package cpu detects the CPU features that sha1cd dispatches on.
//
// It covers only what the assembly implementations need, which keeps
// start up cheap and avoids an external dependency. Every flag is false
// where a feature cannot be detected safely, which selects the generic
// implementation.
package cpu
// X86 holds the features of the current amd64 CPU. All flags are false on
// other architectures.
var X86 struct {
// HasAVX and HasAVX2 are set only when the OS also preserves the YMM
// state, and HasAVX512F only when it preserves the ZMM and opmask state.
HasAVX bool
HasAVX2 bool
HasAVX512F bool
HasBMI1 bool
HasBMI2 bool
HasSHA bool
HasSSSE3 bool
HasSSE41 bool
}
// ARM64 holds the features of the current arm64 CPU. All flags are false on
// other architectures.
var ARM64 struct {
HasSHA1 bool
}
+63
View File
@@ -0,0 +1,63 @@
//go:build !noasm && gc && amd64
package cpu
// cpuid and xgetbv are implemented in cpu_amd64.s.
func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32)
func xgetbv() (eax, edx uint32)
func init() {
const (
// CPUID EAX=1: ECX
ssse3 = 1 << 9
sse41 = 1 << 19
osxsave = 1 << 27
avx = 1 << 28
// CPUID EAX=7, ECX=0: EBX
bmi1 = 1 << 3
avx2 = 1 << 5
bmi2 = 1 << 8
avx512f = 1 << 16
sha = 1 << 29
// XCR0
xmmState = 1 << 1
ymmState = 1 << 2
opmaskState = 1 << 5
zmmHi256State = 1 << 6
hi16ZMMState = 1 << 7
zmmState = opmaskState | zmmHi256State | hi16ZMMState
)
maxID, _, _, _ := cpuid(0, 0)
if maxID < 1 {
return
}
_, _, ecx1, _ := cpuid(1, 0)
X86.HasSSSE3 = ecx1&ssse3 != 0
X86.HasSSE41 = ecx1&sse41 != 0
// VEX encoded instructions also need the OS to preserve the YMM state,
// which XGETBV reports once OSXSAVE says it is available.
// macOS enables the ZMM state lazily, so XCR0 may not report it and
// AVX-512 is then left unused, which is safe.
var osYMM, osZMM bool
if ecx1&(osxsave|avx) == osxsave|avx {
xcr0, _ := xgetbv()
osYMM = xcr0&(xmmState|ymmState) == xmmState|ymmState
osZMM = osYMM && xcr0&zmmState == zmmState
}
X86.HasAVX = osYMM
if maxID < 7 {
return
}
_, ebx7, _, _ := cpuid(7, 0)
X86.HasAVX2 = osYMM && ebx7&avx2 != 0
X86.HasAVX512F = osZMM && ebx7&avx512f != 0
X86.HasBMI1 = ebx7&bmi1 != 0
X86.HasBMI2 = ebx7&bmi2 != 0
X86.HasSHA = ebx7&sha != 0
}
+22
View File
@@ -0,0 +1,22 @@
//go:build !noasm && gc && amd64
#include "textflag.h"
// func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32)
TEXT ·cpuid(SB), NOSPLIT, $0-24
MOVL eaxArg+0(FP), AX
MOVL ecxArg+4(FP), CX
CPUID
MOVL AX, eax+8(FP)
MOVL BX, ebx+12(FP)
MOVL CX, ecx+16(FP)
MOVL DX, edx+20(FP)
RET
// func xgetbv() (eax, edx uint32)
TEXT ·xgetbv(SB), NOSPLIT, $0-8
MOVL $0, CX
XGETBV
MOVL AX, eax+0(FP)
MOVL DX, edx+4(FP)
RET
+9
View File
@@ -0,0 +1,9 @@
//go:build !noasm && gc && arm64 && darwin
package cpu
// Every Apple arm64 chip implements the ARMv8 Cryptographic Extension. The Go
// runtime makes the same assumption for crypto/sha1 on darwin/arm64.
func init() {
ARM64.HasSHA1 = true
}
+12
View File
@@ -0,0 +1,12 @@
//go:build !noasm && gc && arm64 && freebsd
package cpu
// getisar0 is implemented in cpu_arm64_freebsd.s.
func getisar0() uint64
// FreeBSD emulates user space reads of ID_AA64ISAR0_EL1. Its SHA1 field, bits
// [11:8], is non zero when the SHA1 instructions are implemented.
func init() {
ARM64.HasSHA1 = (getisar0()>>8)&0xf != 0
}
+9
View File
@@ -0,0 +1,9 @@
//go:build !noasm && gc && arm64 && freebsd
#include "textflag.h"
// func getisar0() uint64
TEXT ·getisar0(SB), NOSPLIT, $0-8
MRS ID_AA64ISAR0_EL1, R0
MOVD R0, ret+0(FP)
RET
+29
View File
@@ -0,0 +1,29 @@
//go:build !noasm && gc && arm64 && linux
package cpu
import (
"os"
_ "unsafe" // for go:linkname
)
// runtime_getAuxv returns the auxiliary vector the kernel passed to the
// process. The runtime keeps it reachable for golang.org/x/sys/cpu and
// others, see go.dev/issue/57336 and go.dev/issue/67401.
//
//go:linkname runtime_getAuxv runtime.getAuxv
func runtime_getAuxv() []uintptr
// Linux, and so Android, reports the SHA1 instructions through HWCAP, as
// not every kernel lets user space read the ID registers.
func init() {
hwcap, ok := hwcapFromAuxv(runtime_getAuxv())
if !ok {
// The procfs copy may not be readable in restricted environments,
// in which case the generic implementation is used.
if buf, err := os.ReadFile("/proc/self/auxv"); err == nil {
hwcap, _ = hwcapFromProcAuxv(buf)
}
}
ARM64.HasSHA1 = hwcap&hwcapSHA1 != 0
}
+18
View File
@@ -0,0 +1,18 @@
//go:build !noasm && gc && arm64 && windows
package cpu
import "syscall"
// Windows reports the ARMv8 Cryptographic Extension, which includes the SHA1
// instructions, through IsProcessorFeaturePresent.
func init() {
const _PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE = 30
proc := syscall.NewLazyDLL("kernel32.dll").NewProc("IsProcessorFeaturePresent")
if proc.Find() != nil {
return
}
ret, _, _ := proc.Call(_PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE)
ARM64.HasSHA1 = ret != 0
}
+32
View File
@@ -0,0 +1,32 @@
package cpu
import "encoding/binary"
const (
_AT_HWCAP = 16
// hwcapSHA1 is HWCAP_SHA1 on linux/arm64.
hwcapSHA1 = 1 << 5
)
// hwcapFromAuxv returns AT_HWCAP from an auxiliary vector of tag and value
// pairs, and whether it was present.
func hwcapFromAuxv(auxv []uintptr) (uint64, bool) {
for i := 0; i+1 < len(auxv); i += 2 {
if auxv[i] == _AT_HWCAP {
return uint64(auxv[i+1]), true
}
}
return 0, false
}
// hwcapFromProcAuxv does the same for the contents of /proc/self/auxv on a
// 64-bit little endian system.
func hwcapFromProcAuxv(buf []byte) (uint64, bool) {
for ; len(buf) >= 16; buf = buf[16:] {
if binary.LittleEndian.Uint64(buf) == _AT_HWCAP {
return binary.LittleEndian.Uint64(buf[8:]), true
}
}
return 0, false
}
+28 -18
View File
@@ -4,30 +4,39 @@
package sha1cd
import (
"runtime"
"github.com/klauspost/cpuid/v2"
shared "github.com/pjbgf/sha1cd/internal"
"github.com/pjbgf/sha1cd/internal/cpu"
"github.com/pjbgf/sha1cd/ubc"
)
var hasSHANI = (runtime.GOARCH == "amd64" &&
cpuid.CPU.Supports(cpuid.AVX) &&
cpuid.CPU.Supports(cpuid.SHA) &&
cpuid.CPU.Supports(cpuid.SSE3) &&
cpuid.CPU.Supports(cpuid.SSE4))
// hasSHANI reports whether blockAMD64 can run. It uses legacy SSE encodings
// only, so it does not need AVX and also runs on the Goldmont, Goldmont Plus
// and Tremont Atoms, which implement SHA-NI without AVX.
var hasSHANI = cpu.X86.HasSHA && cpu.X86.HasSSSE3 && cpu.X86.HasSSE41
// blockAMD64 hashes the message p into the current state in h.
// blockAMD64 hashes a single chunk of p into the current state in h.
// p must hold at least one whole chunk. Anything beyond the first chunk is
// ignored, as the collision detection the caller runs afterwards inspects m1
// and cs for one chunk only.
// Both m1 and cs are used to store intermediate results which are used by the collision detection logic.
//
//go:noescape
func blockAMD64(h []uint32, p []byte, m1 []uint32, cs [][5]uint32)
func block(dig *digest, p []byte) {
if forceGeneric || !hasSHANI {
switch {
case forceGeneric:
blockGeneric(dig, p)
case hasSHANI:
blockSHANI(dig, p)
case hasAVX2:
blockAVX2(dig, p)
default:
blockGeneric(dig, p)
return
}
}
func blockSHANI(dig *digest, p []byte) {
m1 := [shared.Rounds]uint32{}
cs := [shared.PreStepState][shared.WordBuffers]uint32{}
@@ -37,14 +46,15 @@ func block(dig *digest, p []byte) {
chunk := p[:shared.Chunk]
blockAMD64(dig.h[:], chunk, m1[:], cs[:])
rectifyCompressionState(&m1, &cs)
// Assembly states need repair only when a disturbance vector survives.
if mask := ubc.CalculateDvMask(&m1); mask != 0 {
rectifyCompressionState(&m1, &cs)
if checkCollision(&m1, &cs, &dig.h, mask) {
dig.col = true
col := checkCollision(&m1, &cs, &dig.h)
if col {
dig.col = true
blockAMD64(dig.h[:], chunk, m1[:], cs[:])
blockAMD64(dig.h[:], chunk, m1[:], cs[:])
blockAMD64(dig.h[:], chunk, m1[:], cs[:])
blockAMD64(dig.h[:], chunk, m1[:], cs[:])
}
}
p = p[shared.Chunk:]
+44 -49
View File
@@ -12,27 +12,30 @@
// - https://github.com/golang/go/blob/master/src/crypto/sha1/sha1block_amd64.s
// Reverse the dword order in abcd via PSHUFD then store the 16 bytes in one
// move, instead of issuing four VPEXTRD's that each go through the store port.
// move, instead of issuing four PEXTRD's that each go through the store port.
#define LOADCS(abcd, e, index, target) \
VPSHUFD $0x1B, abcd, X8; \
VMOVDQU X8, ((index*20)+0)(target); \
PSHUFD $0x1B, abcd, X8; \
MOVOU X8, ((index*20)+0)(target); \
MOVL e, ((index*20)+16)(target);
#define LOADM1(m1, index, target) \
VPSHUFD $0x1B, m1, X8; \
VMOVDQU X8, ((index*16)+0)(target);
PSHUFD $0x1B, m1, X8; \
MOVOU X8, ((index*16)+0)(target);
// func blockAMD64(h []uint32, p []byte, m1 []uint32, cs [][5]uint32)
// Requires: AVX, SHA, SSE2, SSE4.1, SSSE3
// Requires: SHA, SSE2, SSE4.1, SSSE3
TEXT ·blockAMD64(SB), NOSPLIT, $80-96
MOVQ h_base+0(FP), DI
MOVQ p_base+24(FP), SI
MOVQ p_len+32(FP), DX
MOVQ m1_base+48(FP), R13
MOVQ cs_base+72(FP), R15
CMPQ DX, $0x00
JEQ done
ADDQ SI, DX
// Truncate the length to whole chunks and skip the block if none is left.
// The caller compresses exactly one chunk per call, so that the collision
// detection it runs afterwards sees this chunk's m1 and cs.
ANDQ $-64, DX
JZ done
// Allocate space on the stack for saving ABCD and E0, and align it to 16 bytes
LEAQ 15(SP), AX
@@ -42,52 +45,51 @@ TEXT ·blockAMD64(SB), NOSPLIT, $80-96
// Load initial hash state
PINSRD $0x03, 16(DI), X5
VMOVDQU (DI), X0
MOVOU (DI), X0
PAND upper_mask<>+0(SB), X5
PSHUFD $0x1b, X0, X0
VMOVDQA shuffle_mask<>+0(SB), X7
MOVO shuffle_mask<>+0(SB), X7
loop:
// Save ABCD and E working values
VMOVDQA X5, (AX)
VMOVDQA X0, 16(AX)
MOVO X5, (AX)
MOVO X0, 16(AX)
// LOAD CS 0
VPEXTRD $3, X5, R12
PEXTRD $3, X5, R12
LOADCS(X0, R12, 0, R15)
// Rounds 0-3
VMOVDQU (SI), X1
MOVOU (SI), X1
PSHUFB X7, X1
PADDD X1, X5
VMOVDQA X0, X6
MOVO X0, X6
SHA1RNDS4 $0x00, X5, X0
LOADM1(X1, 0, R13)
// Rounds 4-7
VMOVDQU 16(SI), X2
MOVOU 16(SI), X2
PSHUFB X7, X2
SHA1NEXTE X2, X6
VMOVDQA X0, X5
MOVO X0, X5
SHA1RNDS4 $0x00, X6, X0
SHA1MSG1 X2, X1
LOADM1(X2, 1, R13)
// Rounds 8-11
VMOVDQU 32(SI), X3
MOVOU 32(SI), X3
PSHUFB X7, X3
SHA1NEXTE X3, X5
VMOVDQA X0, X6
MOVO X0, X6
SHA1RNDS4 $0x00, X5, X0
SHA1MSG1 X3, X2
PXOR X3, X1
LOADM1(X3, 2, R13)
// Rounds 12-15
VMOVDQU 48(SI), X4
MOVOU 48(SI), X4
PSHUFB X7, X4
SHA1NEXTE X4, X6
VMOVDQA X0, X5
MOVO X0, X5
SHA1MSG2 X4, X1
SHA1RNDS4 $0x00, X6, X0
SHA1MSG1 X4, X3
@@ -96,7 +98,7 @@ loop:
// Rounds 16-19
SHA1NEXTE X1, X5
VMOVDQA X0, X6
MOVO X0, X6
SHA1MSG2 X1, X2
SHA1RNDS4 $0x00, X5, X0
SHA1MSG1 X1, X4
@@ -105,7 +107,7 @@ loop:
// Rounds 20-23
SHA1NEXTE X2, X6
VMOVDQA X0, X5
MOVO X0, X5
SHA1MSG2 X2, X3
SHA1RNDS4 $0x01, X6, X0
SHA1MSG1 X2, X1
@@ -114,7 +116,7 @@ loop:
// Rounds 24-27
SHA1NEXTE X3, X5
VMOVDQA X0, X6
MOVO X0, X6
SHA1MSG2 X3, X4
SHA1RNDS4 $0x01, X5, X0
SHA1MSG1 X3, X2
@@ -123,7 +125,7 @@ loop:
// Rounds 28-31
SHA1NEXTE X4, X6
VMOVDQA X0, X5
MOVO X0, X5
SHA1MSG2 X4, X1
SHA1RNDS4 $0x01, X6, X0
SHA1MSG1 X4, X3
@@ -132,7 +134,7 @@ loop:
// Rounds 32-35
SHA1NEXTE X1, X5
VMOVDQA X0, X6
MOVO X0, X6
SHA1MSG2 X1, X2
SHA1RNDS4 $0x01, X5, X0
SHA1MSG1 X1, X4
@@ -141,7 +143,7 @@ loop:
// Rounds 36-39
SHA1NEXTE X2, X6
VMOVDQA X0, X5
MOVO X0, X5
SHA1MSG2 X2, X3
SHA1RNDS4 $0x01, X6, X0
SHA1MSG1 X2, X1
@@ -150,7 +152,7 @@ loop:
// Rounds 40-43
SHA1NEXTE X3, X5
VMOVDQA X0, X6
MOVO X0, X6
SHA1MSG2 X3, X4
SHA1RNDS4 $0x02, X5, X0
SHA1MSG1 X3, X2
@@ -159,7 +161,7 @@ loop:
// Rounds 44-47
SHA1NEXTE X4, X6
VMOVDQA X0, X5
MOVO X0, X5
SHA1MSG2 X4, X1
SHA1RNDS4 $0x02, X6, X0
SHA1MSG1 X4, X3
@@ -168,21 +170,20 @@ loop:
// Rounds 48-51
SHA1NEXTE X1, X5
VMOVDQA X0, X6
MOVO X0, X6
SHA1MSG2 X1, X2
SHA1RNDS4 $0x02, X5, X0
VPEXTRD $0, X5, R12
SHA1MSG1 X1, X4
PXOR X1, X3
LOADM1(X1, 12, R13)
// derive pre-round 56's E out of round 51's A.
VPEXTRD $3, X0, R12
PEXTRD $3, X0, R12
ROLL $30, R12
// Rounds 52-55
SHA1NEXTE X2, X6
VMOVDQA X0, X5
MOVO X0, X5
SHA1MSG2 X2, X3
SHA1RNDS4 $0x02, X6, X0
SHA1MSG1 X2, X1
@@ -194,21 +195,20 @@ loop:
// Rounds 56-59
SHA1NEXTE X3, X5
VMOVDQA X0, X6
MOVO X0, X6
SHA1MSG2 X3, X4
SHA1RNDS4 $0x02, X5, X0
VPEXTRD $0, X5, R12
SHA1MSG1 X3, X2
PXOR X3, X1
LOADM1(X3, 14, R13)
// derive pre-round 64's E out of round 59's A.
VPEXTRD $3, X0, R12
PEXTRD $3, X0, R12
ROLL $30, R12
// Rounds 60-63
SHA1NEXTE X4, X6
VMOVDQA X0, X5
MOVO X0, X5
SHA1MSG2 X4, X1
SHA1RNDS4 $0x03, X6, X0
SHA1MSG1 X4, X3
@@ -220,7 +220,7 @@ loop:
// Rounds 64-67
SHA1NEXTE X1, X5
VMOVDQA X0, X6
MOVO X0, X6
SHA1MSG2 X1, X2
SHA1RNDS4 $0x03, X5, X0
SHA1MSG1 X1, X4
@@ -229,7 +229,7 @@ loop:
// Rounds 68-71
SHA1NEXTE X2, X6
VMOVDQA X0, X5
MOVO X0, X5
SHA1MSG2 X2, X3
SHA1RNDS4 $0x03, X6, X0
PXOR X2, X4
@@ -237,14 +237,14 @@ loop:
// Rounds 72-75
SHA1NEXTE X3, X5
VMOVDQA X0, X6
MOVO X0, X6
SHA1MSG2 X3, X4
SHA1RNDS4 $0x03, X5, X0
LOADM1(X3, 18, R13)
// Rounds 76-79
SHA1NEXTE X4, X6
VMOVDQA X0, X5
MOVO X0, X5
SHA1RNDS4 $0x03, X6, X0
LOADM1(X4, 19, R13)
@@ -252,14 +252,9 @@ loop:
SHA1NEXTE (AX), X5
PADDD 16(AX), X0
// Check if we are done, if not return to the loop
ADDQ $0x40, SI
CMPQ SI, DX
JNE loop
// Write the hash state back to digest
PSHUFD $0x1b, X0, X0
VMOVDQU X0, (DI)
MOVOU X0, (DI)
PEXTRD $0x03, X5, 16(DI)
done:
+15 -11
View File
@@ -4,15 +4,17 @@
package sha1cd
import (
"runtime"
"github.com/klauspost/cpuid/v2"
shared "github.com/pjbgf/sha1cd/internal"
"github.com/pjbgf/sha1cd/internal/cpu"
"github.com/pjbgf/sha1cd/ubc"
)
var hasSHA1 = (runtime.GOARCH == "arm64" && cpuid.CPU.Supports(cpuid.SHA1))
var hasSHA1 = cpu.ARM64.HasSHA1
// blockARM64 hashes the message p into the current state in h.
// blockARM64 hashes a single chunk of p into the current state in h.
// p must hold at least one whole chunk. Anything beyond the first chunk is
// ignored, as the collision detection the caller runs afterwards inspects m1
// and cs for one chunk only.
// Both m1 and cs are used to store intermediate results which are used by the collision detection logic.
//
//go:noescape
@@ -34,13 +36,15 @@ func block(dig *digest, p []byte) {
blockARM64(dig.h[:], chunk, m1[:], cs[:])
rectifyCompressionState(&m1, &cs)
col := checkCollision(&m1, &cs, &dig.h)
if col {
dig.col = true
// Assembly states need repair only when a disturbance vector survives.
if mask := ubc.CalculateDvMask(&m1); mask != 0 {
rectifyCompressionState(&m1, &cs)
if checkCollision(&m1, &cs, &dig.h, mask) {
dig.col = true
blockARM64(dig.h[:], chunk, m1[:], cs[:])
blockARM64(dig.h[:], chunk, m1[:], cs[:])
blockARM64(dig.h[:], chunk, m1[:], cs[:])
blockARM64(dig.h[:], chunk, m1[:], cs[:])
}
}
p = p[shared.Chunk:]
+2 -4
View File
@@ -37,15 +37,13 @@ TEXT ·blockARM64(SB), NOSPLIT, $80-96
LSR $6, R2, R2
LSL $6, R2, R2
ADD R16, R2, R21
VLD1.P 16(R0), [V0.S4]
FMOVS (R0), F20
SUB $16, R0, R0
loop:
CMP R16, R21
BLS end
// The caller passes exactly one block; skip hashing only if p is shorter.
CBZ R2, end
// Load block (p) into 16-bytes vectors.
VLD1.P 16(R1), [V4.B16]
+100
View File
@@ -0,0 +1,100 @@
//go:build !noasm && gc && arm64 && !amd64 && sha1cd_asmtest
#include "textflag.h"
// callBlockARM64DirtyRegs calls blockARM64 with every general-purpose and
// vector register the caller does not own set to all ones. It is only built
// with the sha1cd_asmtest tag, to catch the assembly reading a register before
// writing it.
//
// func callBlockARM64DirtyRegs(h []uint32, p []byte, m1 []uint32, cs [][5]uint32)
TEXT ·callBlockARM64DirtyRegs(SB), NOSPLIT, $104-96
MOVD h_base+0(FP), R0
MOVD R0, 8(RSP)
MOVD h_len+8(FP), R0
MOVD R0, 16(RSP)
MOVD h_cap+16(FP), R0
MOVD R0, 24(RSP)
MOVD p_base+24(FP), R0
MOVD R0, 32(RSP)
MOVD p_len+32(FP), R0
MOVD R0, 40(RSP)
MOVD p_cap+40(FP), R0
MOVD R0, 48(RSP)
MOVD m1_base+48(FP), R0
MOVD R0, 56(RSP)
MOVD m1_len+56(FP), R0
MOVD R0, 64(RSP)
MOVD m1_cap+64(FP), R0
MOVD R0, 72(RSP)
MOVD cs_base+72(FP), R0
MOVD R0, 80(RSP)
MOVD cs_len+80(FP), R0
MOVD R0, 88(RSP)
MOVD cs_cap+88(FP), R0
MOVD R0, 96(RSP)
// R18 is reserved by the platform, R27 by the assembler, R28 holds g,
// R29 is the frame pointer and R30 the link register.
MOVD $-1, R0
MOVD R0, R1
MOVD R0, R2
MOVD R0, R3
MOVD R0, R4
MOVD R0, R5
MOVD R0, R6
MOVD R0, R7
MOVD R0, R8
MOVD R0, R9
MOVD R0, R10
MOVD R0, R11
MOVD R0, R12
MOVD R0, R13
MOVD R0, R14
MOVD R0, R15
MOVD R0, R16
MOVD R0, R17
MOVD R0, R19
MOVD R0, R20
MOVD R0, R21
MOVD R0, R22
MOVD R0, R23
MOVD R0, R24
MOVD R0, R25
MOVD R0, R26
VMOVI $0xff, V0.B16
VMOVI $0xff, V1.B16
VMOVI $0xff, V2.B16
VMOVI $0xff, V3.B16
VMOVI $0xff, V4.B16
VMOVI $0xff, V5.B16
VMOVI $0xff, V6.B16
VMOVI $0xff, V7.B16
VMOVI $0xff, V8.B16
VMOVI $0xff, V9.B16
VMOVI $0xff, V10.B16
VMOVI $0xff, V11.B16
VMOVI $0xff, V12.B16
VMOVI $0xff, V13.B16
VMOVI $0xff, V14.B16
VMOVI $0xff, V15.B16
VMOVI $0xff, V16.B16
VMOVI $0xff, V17.B16
VMOVI $0xff, V18.B16
VMOVI $0xff, V19.B16
VMOVI $0xff, V20.B16
VMOVI $0xff, V21.B16
VMOVI $0xff, V22.B16
VMOVI $0xff, V23.B16
VMOVI $0xff, V24.B16
VMOVI $0xff, V25.B16
VMOVI $0xff, V26.B16
VMOVI $0xff, V27.B16
VMOVI $0xff, V28.B16
VMOVI $0xff, V29.B16
VMOVI $0xff, V30.B16
VMOVI $0xff, V31.B16
BL ·blockARM64(SB)
RET
+55
View File
@@ -0,0 +1,55 @@
//go:build !noasm && gc && amd64
package sha1cd
import (
shared "github.com/pjbgf/sha1cd/internal"
"github.com/pjbgf/sha1cd/internal/cpu"
"github.com/pjbgf/sha1cd/ubc"
)
// hasAVX2 reports whether blockAVX2 can run. It is the fallback for CPUs
// without SHA-NI, such as Intel's big cores before Ice Lake.
var hasAVX2 = cpu.X86.HasAVX2 && cpu.X86.HasBMI1 && cpu.X86.HasBMI2
// scheduleAVX2 expands the message schedules of the blocks at pa and pb, which
// may be the same, into m1a and m1b.
//
//go:noescape
func scheduleAVX2(pa, pb *byte, m1a, m1b *[shared.Rounds]uint32)
// roundsBMI2 compresses the block whose schedule is in m1 into h, and stores
// the states before steps 0, 58 and 65 into cs.
//
//go:noescape
func roundsBMI2(h *[shared.WordBuffers]uint32, m1 *[shared.Rounds]uint32,
cs *[shared.PreStepState][shared.WordBuffers]uint32)
func blockAVX2(dig *digest, p []byte) {
var m1 [2][shared.Rounds]uint32
cs := [shared.PreStepState][shared.WordBuffers]uint32{}
for len(p) >= shared.Chunk {
// The schedules do not depend on the chaining state, so expand two
// blocks at once. The rounds must still run one block at a time, as a
// detected collision changes the state the next block starts from.
n, pb := 1, p
if len(p) >= 2*shared.Chunk {
n, pb = 2, p[shared.Chunk:]
}
scheduleAVX2(&p[0], &pb[0], &m1[0], &m1[1])
for j := 0; j < n; j++ {
w := &m1[j]
roundsBMI2(&dig.h, w, &cs)
if mask := ubc.CalculateDvMask(w); mask != 0 && checkCollision(w, &cs, &dig.h, mask) {
dig.col = true
roundsBMI2(&dig.h, w, &cs)
roundsBMI2(&dig.h, w, &cs)
}
}
p = p[n*shared.Chunk:]
}
}
+415
View File
@@ -0,0 +1,415 @@
//go:build !noasm && gc && amd64
#include "textflag.h"
// The fallback for CPUs without SHA-NI. The message schedule is expanded with
// AVX2 for two blocks at a time, one per 128-bit lane, as it does not depend
// on the chaining state. The rounds then run one block at a time with scalar
// BMI instructions, so that the collision detection can run between blocks
// and gets the exact states before steps 58 and 65, with no repair needed.
// func scheduleAVX2(pa, pb *byte, m1a, m1b *[80]uint32)
// Requires: AVX, AVX2
//
// Expands the blocks at pa and pb, which may be the same, into m1a and m1b.
// Y0-Y7 hold the last eight groups of four words, group g in Y(g%8).
TEXT ·scheduleAVX2(SB), NOSPLIT, $0-32
MOVQ pa+0(FP), AX
MOVQ pb+8(FP), BX
MOVQ m1a+16(FP), CX
MOVQ m1b+24(FP), DX
VBROADCASTI128 bswap_mask<>(SB), Y13
// W[0..3] loaded from the blocks
VMOVDQU 0(AX), X0
VINSERTI128 $1, 0(BX), Y0, Y0
VPSHUFB Y13, Y0, Y0
VMOVDQU X0, 0(CX)
VEXTRACTI128 $1, Y0, 0(DX)
// W[4..7] loaded from the blocks
VMOVDQU 16(AX), X1
VINSERTI128 $1, 16(BX), Y1, Y1
VPSHUFB Y13, Y1, Y1
VMOVDQU X1, 16(CX)
VEXTRACTI128 $1, Y1, 16(DX)
// W[8..11] loaded from the blocks
VMOVDQU 32(AX), X2
VINSERTI128 $1, 32(BX), Y2, Y2
VPSHUFB Y13, Y2, Y2
VMOVDQU X2, 32(CX)
VEXTRACTI128 $1, Y2, 32(DX)
// W[12..15] loaded from the blocks
VMOVDQU 48(AX), X3
VINSERTI128 $1, 48(BX), Y3, Y3
VPSHUFB Y13, Y3, Y3
VMOVDQU X3, 48(CX)
VEXTRACTI128 $1, Y3, 48(DX)
// W[16..19] = rol1(W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16]). W[i+3] needs
// W[i] from this group, which is added in afterwards.
VPALIGNR $8, Y0, Y1, Y8 // W[i-14..i-11]
VPXOR Y0, Y8, Y8 // W[i-16..i-13]
VPXOR Y2, Y8, Y8 // W[i-8..i-5]
VPSRLDQ $4, Y3, Y9 // W[i-3..i-1], 0
VPXOR Y9, Y8, Y8
VPSRLD $31, Y8, Y9
VPSLLD $1, Y8, Y4
VPOR Y9, Y4, Y4
VPSLLDQ $12, Y8, Y10 // 0, 0, 0, the input to W[i]
VPSRLD $30, Y10, Y9
VPSLLD $2, Y10, Y10
VPOR Y9, Y10, Y10 // rol1(W[i]) in the lane of W[i+3]
VPXOR Y10, Y4, Y4
VMOVDQU X4, 64(CX)
VEXTRACTI128 $1, Y4, 64(DX)
// W[20..23] = rol1(W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16]). W[i+3] needs
// W[i] from this group, which is added in afterwards.
VPALIGNR $8, Y1, Y2, Y8 // W[i-14..i-11]
VPXOR Y1, Y8, Y8 // W[i-16..i-13]
VPXOR Y3, Y8, Y8 // W[i-8..i-5]
VPSRLDQ $4, Y4, Y9 // W[i-3..i-1], 0
VPXOR Y9, Y8, Y8
VPSRLD $31, Y8, Y9
VPSLLD $1, Y8, Y5
VPOR Y9, Y5, Y5
VPSLLDQ $12, Y8, Y10 // 0, 0, 0, the input to W[i]
VPSRLD $30, Y10, Y9
VPSLLD $2, Y10, Y10
VPOR Y9, Y10, Y10 // rol1(W[i]) in the lane of W[i+3]
VPXOR Y10, Y5, Y5
VMOVDQU X5, 80(CX)
VEXTRACTI128 $1, Y5, 80(DX)
// W[24..27] = rol1(W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16]). W[i+3] needs
// W[i] from this group, which is added in afterwards.
VPALIGNR $8, Y2, Y3, Y8 // W[i-14..i-11]
VPXOR Y2, Y8, Y8 // W[i-16..i-13]
VPXOR Y4, Y8, Y8 // W[i-8..i-5]
VPSRLDQ $4, Y5, Y9 // W[i-3..i-1], 0
VPXOR Y9, Y8, Y8
VPSRLD $31, Y8, Y9
VPSLLD $1, Y8, Y6
VPOR Y9, Y6, Y6
VPSLLDQ $12, Y8, Y10 // 0, 0, 0, the input to W[i]
VPSRLD $30, Y10, Y9
VPSLLD $2, Y10, Y10
VPOR Y9, Y10, Y10 // rol1(W[i]) in the lane of W[i+3]
VPXOR Y10, Y6, Y6
VMOVDQU X6, 96(CX)
VEXTRACTI128 $1, Y6, 96(DX)
// W[28..31] = rol1(W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16]). W[i+3] needs
// W[i] from this group, which is added in afterwards.
VPALIGNR $8, Y3, Y4, Y8 // W[i-14..i-11]
VPXOR Y3, Y8, Y8 // W[i-16..i-13]
VPXOR Y5, Y8, Y8 // W[i-8..i-5]
VPSRLDQ $4, Y6, Y9 // W[i-3..i-1], 0
VPXOR Y9, Y8, Y8
VPSRLD $31, Y8, Y9
VPSLLD $1, Y8, Y7
VPOR Y9, Y7, Y7
VPSLLDQ $12, Y8, Y10 // 0, 0, 0, the input to W[i]
VPSRLD $30, Y10, Y9
VPSLLD $2, Y10, Y10
VPOR Y9, Y10, Y10 // rol1(W[i]) in the lane of W[i+3]
VPXOR Y10, Y7, Y7
VMOVDQU X7, 112(CX)
VEXTRACTI128 $1, Y7, 112(DX)
// W[32..35] = rol2(W[i-6] ^ W[i-16] ^ W[i-28] ^ W[i-32])
VPALIGNR $8, Y6, Y7, Y8 // W[i-6..i-3]
VPXOR Y4, Y8, Y8 // W[i-16..i-13]
VPXOR Y1, Y8, Y8 // W[i-28..i-25]
VPXOR Y0, Y8, Y8 // W[i-32..i-29]
VPSRLD $30, Y8, Y9
VPSLLD $2, Y8, Y0
VPOR Y9, Y0, Y0
VMOVDQU X0, 128(CX)
VEXTRACTI128 $1, Y0, 128(DX)
// W[36..39] = rol2(W[i-6] ^ W[i-16] ^ W[i-28] ^ W[i-32])
VPALIGNR $8, Y7, Y0, Y8 // W[i-6..i-3]
VPXOR Y5, Y8, Y8 // W[i-16..i-13]
VPXOR Y2, Y8, Y8 // W[i-28..i-25]
VPXOR Y1, Y8, Y8 // W[i-32..i-29]
VPSRLD $30, Y8, Y9
VPSLLD $2, Y8, Y1
VPOR Y9, Y1, Y1
VMOVDQU X1, 144(CX)
VEXTRACTI128 $1, Y1, 144(DX)
// W[40..43] = rol2(W[i-6] ^ W[i-16] ^ W[i-28] ^ W[i-32])
VPALIGNR $8, Y0, Y1, Y8 // W[i-6..i-3]
VPXOR Y6, Y8, Y8 // W[i-16..i-13]
VPXOR Y3, Y8, Y8 // W[i-28..i-25]
VPXOR Y2, Y8, Y8 // W[i-32..i-29]
VPSRLD $30, Y8, Y9
VPSLLD $2, Y8, Y2
VPOR Y9, Y2, Y2
VMOVDQU X2, 160(CX)
VEXTRACTI128 $1, Y2, 160(DX)
// W[44..47] = rol2(W[i-6] ^ W[i-16] ^ W[i-28] ^ W[i-32])
VPALIGNR $8, Y1, Y2, Y8 // W[i-6..i-3]
VPXOR Y7, Y8, Y8 // W[i-16..i-13]
VPXOR Y4, Y8, Y8 // W[i-28..i-25]
VPXOR Y3, Y8, Y8 // W[i-32..i-29]
VPSRLD $30, Y8, Y9
VPSLLD $2, Y8, Y3
VPOR Y9, Y3, Y3
VMOVDQU X3, 176(CX)
VEXTRACTI128 $1, Y3, 176(DX)
// W[48..51] = rol2(W[i-6] ^ W[i-16] ^ W[i-28] ^ W[i-32])
VPALIGNR $8, Y2, Y3, Y8 // W[i-6..i-3]
VPXOR Y0, Y8, Y8 // W[i-16..i-13]
VPXOR Y5, Y8, Y8 // W[i-28..i-25]
VPXOR Y4, Y8, Y8 // W[i-32..i-29]
VPSRLD $30, Y8, Y9
VPSLLD $2, Y8, Y4
VPOR Y9, Y4, Y4
VMOVDQU X4, 192(CX)
VEXTRACTI128 $1, Y4, 192(DX)
// W[52..55] = rol2(W[i-6] ^ W[i-16] ^ W[i-28] ^ W[i-32])
VPALIGNR $8, Y3, Y4, Y8 // W[i-6..i-3]
VPXOR Y1, Y8, Y8 // W[i-16..i-13]
VPXOR Y6, Y8, Y8 // W[i-28..i-25]
VPXOR Y5, Y8, Y8 // W[i-32..i-29]
VPSRLD $30, Y8, Y9
VPSLLD $2, Y8, Y5
VPOR Y9, Y5, Y5
VMOVDQU X5, 208(CX)
VEXTRACTI128 $1, Y5, 208(DX)
// W[56..59] = rol2(W[i-6] ^ W[i-16] ^ W[i-28] ^ W[i-32])
VPALIGNR $8, Y4, Y5, Y8 // W[i-6..i-3]
VPXOR Y2, Y8, Y8 // W[i-16..i-13]
VPXOR Y7, Y8, Y8 // W[i-28..i-25]
VPXOR Y6, Y8, Y8 // W[i-32..i-29]
VPSRLD $30, Y8, Y9
VPSLLD $2, Y8, Y6
VPOR Y9, Y6, Y6
VMOVDQU X6, 224(CX)
VEXTRACTI128 $1, Y6, 224(DX)
// W[60..63] = rol2(W[i-6] ^ W[i-16] ^ W[i-28] ^ W[i-32])
VPALIGNR $8, Y5, Y6, Y8 // W[i-6..i-3]
VPXOR Y3, Y8, Y8 // W[i-16..i-13]
VPXOR Y0, Y8, Y8 // W[i-28..i-25]
VPXOR Y7, Y8, Y8 // W[i-32..i-29]
VPSRLD $30, Y8, Y9
VPSLLD $2, Y8, Y7
VPOR Y9, Y7, Y7
VMOVDQU X7, 240(CX)
VEXTRACTI128 $1, Y7, 240(DX)
// W[64..67] = rol2(W[i-6] ^ W[i-16] ^ W[i-28] ^ W[i-32])
VPALIGNR $8, Y6, Y7, Y8 // W[i-6..i-3]
VPXOR Y4, Y8, Y8 // W[i-16..i-13]
VPXOR Y1, Y8, Y8 // W[i-28..i-25]
VPXOR Y0, Y8, Y8 // W[i-32..i-29]
VPSRLD $30, Y8, Y9
VPSLLD $2, Y8, Y0
VPOR Y9, Y0, Y0
VMOVDQU X0, 256(CX)
VEXTRACTI128 $1, Y0, 256(DX)
// W[68..71] = rol2(W[i-6] ^ W[i-16] ^ W[i-28] ^ W[i-32])
VPALIGNR $8, Y7, Y0, Y8 // W[i-6..i-3]
VPXOR Y5, Y8, Y8 // W[i-16..i-13]
VPXOR Y2, Y8, Y8 // W[i-28..i-25]
VPXOR Y1, Y8, Y8 // W[i-32..i-29]
VPSRLD $30, Y8, Y9
VPSLLD $2, Y8, Y1
VPOR Y9, Y1, Y1
VMOVDQU X1, 272(CX)
VEXTRACTI128 $1, Y1, 272(DX)
// W[72..75] = rol2(W[i-6] ^ W[i-16] ^ W[i-28] ^ W[i-32])
VPALIGNR $8, Y0, Y1, Y8 // W[i-6..i-3]
VPXOR Y6, Y8, Y8 // W[i-16..i-13]
VPXOR Y3, Y8, Y8 // W[i-28..i-25]
VPXOR Y2, Y8, Y8 // W[i-32..i-29]
VPSRLD $30, Y8, Y9
VPSLLD $2, Y8, Y2
VPOR Y9, Y2, Y2
VMOVDQU X2, 288(CX)
VEXTRACTI128 $1, Y2, 288(DX)
// W[76..79] = rol2(W[i-6] ^ W[i-16] ^ W[i-28] ^ W[i-32])
VPALIGNR $8, Y1, Y2, Y8 // W[i-6..i-3]
VPXOR Y7, Y8, Y8 // W[i-16..i-13]
VPXOR Y4, Y8, Y8 // W[i-28..i-25]
VPXOR Y3, Y8, Y8 // W[i-32..i-29]
VPSRLD $30, Y8, Y9
VPSLLD $2, Y8, Y3
VPOR Y9, Y3, Y3
VMOVDQU X3, 304(CX)
VEXTRACTI128 $1, Y3, 304(DX)
VZEROUPPER
RET
// Each round adds W[i], K, f(b, c, d) and rol5(a) into e and rotates b by 30.
// The callers rotate the register names instead of moving values around, and
// rol5(a) goes in last, as a is the only input the previous round produced.
#define ROUND_CH(a, b, c, d, e, i) \
ADDL ((i)*4)(SI), e; \
ADDL $0x5a827999, e; \
ANDNL d, b, AX; \
MOVL c, BX; \
ANDL b, BX; \
ADDL AX, e; \
ADDL BX, e; \
RORXL $27, a, CX; \
RORXL $2, b, b; \
ADDL CX, e
#define ROUND_PARITY(a, b, c, d, e, i, k) \
ADDL ((i)*4)(SI), e; \
ADDL k, e; \
MOVL b, AX; \
XORL c, AX; \
XORL d, AX; \
ADDL AX, e; \
RORXL $27, a, CX; \
RORXL $2, b, b; \
ADDL CX, e
// maj(b, c, d) = (b & c) + ((b ^ c) & d), as the two never share a bit.
#define ROUND_MAJ(a, b, c, d, e, i) \
ADDL ((i)*4)(SI), e; \
ADDL $0x8f1bbcdc, e; \
MOVL b, AX; \
XORL c, AX; \
ANDL d, AX; \
MOVL b, BX; \
ANDL c, BX; \
ADDL AX, e; \
ADDL BX, e; \
RORXL $27, a, CX; \
RORXL $2, b, b; \
ADDL CX, e
#define SAVECS(a, b, c, d, e, index) \
MOVL a, ((index)*20+0)(DX); \
MOVL b, ((index)*20+4)(DX); \
MOVL c, ((index)*20+8)(DX); \
MOVL d, ((index)*20+12)(DX); \
MOVL e, ((index)*20+16)(DX)
// func roundsBMI2(h *[5]uint32, m1 *[80]uint32, cs *[3][5]uint32)
// Requires: BMI1, BMI2
//
// Compresses the block whose schedule is in m1 into h, storing the states
// before steps 0, 58 and 65 into cs.
TEXT ·roundsBMI2(SB), NOSPLIT, $0-24
MOVQ h+0(FP), DI
MOVQ m1+8(FP), SI
MOVQ cs+16(FP), DX
MOVL 0(DI), R8
MOVL 4(DI), R9
MOVL 8(DI), R10
MOVL 12(DI), R11
MOVL 16(DI), R12
SAVECS(R8, R9, R10, R11, R12, 0)
ROUND_CH(R8, R9, R10, R11, R12, 0)
ROUND_CH(R12, R8, R9, R10, R11, 1)
ROUND_CH(R11, R12, R8, R9, R10, 2)
ROUND_CH(R10, R11, R12, R8, R9, 3)
ROUND_CH(R9, R10, R11, R12, R8, 4)
ROUND_CH(R8, R9, R10, R11, R12, 5)
ROUND_CH(R12, R8, R9, R10, R11, 6)
ROUND_CH(R11, R12, R8, R9, R10, 7)
ROUND_CH(R10, R11, R12, R8, R9, 8)
ROUND_CH(R9, R10, R11, R12, R8, 9)
ROUND_CH(R8, R9, R10, R11, R12, 10)
ROUND_CH(R12, R8, R9, R10, R11, 11)
ROUND_CH(R11, R12, R8, R9, R10, 12)
ROUND_CH(R10, R11, R12, R8, R9, 13)
ROUND_CH(R9, R10, R11, R12, R8, 14)
ROUND_CH(R8, R9, R10, R11, R12, 15)
ROUND_CH(R12, R8, R9, R10, R11, 16)
ROUND_CH(R11, R12, R8, R9, R10, 17)
ROUND_CH(R10, R11, R12, R8, R9, 18)
ROUND_CH(R9, R10, R11, R12, R8, 19)
ROUND_PARITY(R8, R9, R10, R11, R12, 20, $0x6ed9eba1)
ROUND_PARITY(R12, R8, R9, R10, R11, 21, $0x6ed9eba1)
ROUND_PARITY(R11, R12, R8, R9, R10, 22, $0x6ed9eba1)
ROUND_PARITY(R10, R11, R12, R8, R9, 23, $0x6ed9eba1)
ROUND_PARITY(R9, R10, R11, R12, R8, 24, $0x6ed9eba1)
ROUND_PARITY(R8, R9, R10, R11, R12, 25, $0x6ed9eba1)
ROUND_PARITY(R12, R8, R9, R10, R11, 26, $0x6ed9eba1)
ROUND_PARITY(R11, R12, R8, R9, R10, 27, $0x6ed9eba1)
ROUND_PARITY(R10, R11, R12, R8, R9, 28, $0x6ed9eba1)
ROUND_PARITY(R9, R10, R11, R12, R8, 29, $0x6ed9eba1)
ROUND_PARITY(R8, R9, R10, R11, R12, 30, $0x6ed9eba1)
ROUND_PARITY(R12, R8, R9, R10, R11, 31, $0x6ed9eba1)
ROUND_PARITY(R11, R12, R8, R9, R10, 32, $0x6ed9eba1)
ROUND_PARITY(R10, R11, R12, R8, R9, 33, $0x6ed9eba1)
ROUND_PARITY(R9, R10, R11, R12, R8, 34, $0x6ed9eba1)
ROUND_PARITY(R8, R9, R10, R11, R12, 35, $0x6ed9eba1)
ROUND_PARITY(R12, R8, R9, R10, R11, 36, $0x6ed9eba1)
ROUND_PARITY(R11, R12, R8, R9, R10, 37, $0x6ed9eba1)
ROUND_PARITY(R10, R11, R12, R8, R9, 38, $0x6ed9eba1)
ROUND_PARITY(R9, R10, R11, R12, R8, 39, $0x6ed9eba1)
ROUND_MAJ(R8, R9, R10, R11, R12, 40)
ROUND_MAJ(R12, R8, R9, R10, R11, 41)
ROUND_MAJ(R11, R12, R8, R9, R10, 42)
ROUND_MAJ(R10, R11, R12, R8, R9, 43)
ROUND_MAJ(R9, R10, R11, R12, R8, 44)
ROUND_MAJ(R8, R9, R10, R11, R12, 45)
ROUND_MAJ(R12, R8, R9, R10, R11, 46)
ROUND_MAJ(R11, R12, R8, R9, R10, 47)
ROUND_MAJ(R10, R11, R12, R8, R9, 48)
ROUND_MAJ(R9, R10, R11, R12, R8, 49)
ROUND_MAJ(R8, R9, R10, R11, R12, 50)
ROUND_MAJ(R12, R8, R9, R10, R11, 51)
ROUND_MAJ(R11, R12, R8, R9, R10, 52)
ROUND_MAJ(R10, R11, R12, R8, R9, 53)
ROUND_MAJ(R9, R10, R11, R12, R8, 54)
ROUND_MAJ(R8, R9, R10, R11, R12, 55)
ROUND_MAJ(R12, R8, R9, R10, R11, 56)
ROUND_MAJ(R11, R12, R8, R9, R10, 57)
SAVECS(R10, R11, R12, R8, R9, 1)
ROUND_MAJ(R10, R11, R12, R8, R9, 58)
ROUND_MAJ(R9, R10, R11, R12, R8, 59)
ROUND_PARITY(R8, R9, R10, R11, R12, 60, $0xca62c1d6)
ROUND_PARITY(R12, R8, R9, R10, R11, 61, $0xca62c1d6)
ROUND_PARITY(R11, R12, R8, R9, R10, 62, $0xca62c1d6)
ROUND_PARITY(R10, R11, R12, R8, R9, 63, $0xca62c1d6)
ROUND_PARITY(R9, R10, R11, R12, R8, 64, $0xca62c1d6)
SAVECS(R8, R9, R10, R11, R12, 2)
ROUND_PARITY(R8, R9, R10, R11, R12, 65, $0xca62c1d6)
ROUND_PARITY(R12, R8, R9, R10, R11, 66, $0xca62c1d6)
ROUND_PARITY(R11, R12, R8, R9, R10, 67, $0xca62c1d6)
ROUND_PARITY(R10, R11, R12, R8, R9, 68, $0xca62c1d6)
ROUND_PARITY(R9, R10, R11, R12, R8, 69, $0xca62c1d6)
ROUND_PARITY(R8, R9, R10, R11, R12, 70, $0xca62c1d6)
ROUND_PARITY(R12, R8, R9, R10, R11, 71, $0xca62c1d6)
ROUND_PARITY(R11, R12, R8, R9, R10, 72, $0xca62c1d6)
ROUND_PARITY(R10, R11, R12, R8, R9, 73, $0xca62c1d6)
ROUND_PARITY(R9, R10, R11, R12, R8, 74, $0xca62c1d6)
ROUND_PARITY(R8, R9, R10, R11, R12, 75, $0xca62c1d6)
ROUND_PARITY(R12, R8, R9, R10, R11, 76, $0xca62c1d6)
ROUND_PARITY(R11, R12, R8, R9, R10, 77, $0xca62c1d6)
ROUND_PARITY(R10, R11, R12, R8, R9, 78, $0xca62c1d6)
ROUND_PARITY(R9, R10, R11, R12, R8, 79, $0xca62c1d6)
ADDL R8, 0(DI)
ADDL R9, 4(DI)
ADDL R10, 8(DI)
ADDL R11, 12(DI)
ADDL R12, 16(DI)
RET
// Swaps the bytes of each word, as SHA-1 reads the block big endian.
DATA bswap_mask<>+0(SB)/8, $0x0405060700010203
DATA bswap_mask<>+8(SB)/8, $0x0c0d0e0f08090a0b
GLOBL bswap_mask<>(SB), RODATA|NOPTR, $16
+32 -46
View File
@@ -20,7 +20,9 @@ var forceGeneric bool
// blockGeneric is a portable, pure Go version of the SHA-1 block step.
// It's used by sha1block_generic.go and tests.
func blockGeneric(dig *digest, p []byte) {
var w [16]uint32
// Expand directly into the schedule retained for collision detection.
// Every word is overwritten for each block, including rehashes.
var m1 [shared.Rounds]uint32
// cs stores the pre-step compression state for only the steps required for the
// collision detection, which are 0, 58 and 65.
@@ -29,7 +31,6 @@ func blockGeneric(dig *digest, p []byte) {
h0, h1, h2, h3, h4 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4]
for len(p) >= shared.Chunk {
m1 := [shared.Rounds]uint32{}
hi := 1
// Collision attacks are thwarted by hashing a detected near-collision block 3 times.
@@ -51,36 +52,27 @@ func blockGeneric(dig *digest, p []byte) {
for ; i < 16; i++ {
// load step
j := i * 4
w[i] = uint32(p[j])<<24 | uint32(p[j+1])<<16 | uint32(p[j+2])<<8 | uint32(p[j+3])
m1[i] = uint32(p[j])<<24 | uint32(p[j+1])<<16 | uint32(p[j+2])<<8 | uint32(p[j+3])
f := b&c | (^b)&d
t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + shared.K0
t := bits.RotateLeft32(a, 5) + f + e + m1[i] + shared.K0
a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d
// Store compression state for the collision detection.
m1[i] = w[i&0xf]
}
for ; i < 20; i++ {
tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf]
w[i&0xf] = tmp<<1 | tmp>>(32-1)
tmp := m1[i-3] ^ m1[i-8] ^ m1[i-14] ^ m1[i-16]
m1[i] = tmp<<1 | tmp>>(32-1)
f := b&c | (^b)&d
t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + shared.K0
t := bits.RotateLeft32(a, 5) + f + e + m1[i] + shared.K0
a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d
// Store compression state for the collision detection.
m1[i] = w[i&0xf]
}
for ; i < 40; i++ {
tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf]
w[i&0xf] = tmp<<1 | tmp>>(32-1)
tmp := m1[i-3] ^ m1[i-8] ^ m1[i-14] ^ m1[i-16]
m1[i] = tmp<<1 | tmp>>(32-1)
f := b ^ c ^ d
t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + shared.K1
t := bits.RotateLeft32(a, 5) + f + e + m1[i] + shared.K1
a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d
// Store compression state for the collision detection.
m1[i] = w[i&0xf]
}
for ; i < 60; i++ {
if i == 58 {
@@ -88,15 +80,12 @@ func blockGeneric(dig *digest, p []byte) {
cs[1] = [shared.WordBuffers]uint32{a, b, c, d, e}
}
tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf]
w[i&0xf] = tmp<<1 | tmp>>(32-1)
tmp := m1[i-3] ^ m1[i-8] ^ m1[i-14] ^ m1[i-16]
m1[i] = tmp<<1 | tmp>>(32-1)
f := ((b | c) & d) | (b & c)
t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + shared.K2
t := bits.RotateLeft32(a, 5) + f + e + m1[i] + shared.K2
a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d
// Store compression state for the collision detection.
m1[i] = w[i&0xf]
}
for ; i < 80; i++ {
if i == 65 {
@@ -104,15 +93,12 @@ func blockGeneric(dig *digest, p []byte) {
cs[2] = [shared.WordBuffers]uint32{a, b, c, d, e}
}
tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf]
w[i&0xf] = tmp<<1 | tmp>>(32-1)
tmp := m1[i-3] ^ m1[i-8] ^ m1[i-14] ^ m1[i-16]
m1[i] = tmp<<1 | tmp>>(32-1)
f := b ^ c ^ d
t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + shared.K3
t := bits.RotateLeft32(a, 5) + f + e + m1[i] + shared.K3
a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d
// Store compression state for the collision detection.
m1[i] = w[i&0xf]
}
h0 += a
@@ -128,7 +114,7 @@ func blockGeneric(dig *digest, p []byte) {
if hi == 1 {
h := [shared.WordBuffers]uint32{h0, h1, h2, h3, h4}
col := checkCollision(&m1, &cs, &h)
col := checkCollision(&m1, &cs, &h, ubc.CalculateDvMask(&m1))
if col {
dig.col = true
hi++
@@ -147,8 +133,9 @@ func checkCollision(
m1 *[shared.Rounds]uint32,
cs *[shared.PreStepState][shared.WordBuffers]uint32,
h *[shared.WordBuffers]uint32,
mask uint32,
) bool {
if mask := ubc.CalculateDvMask(m1); mask != 0 {
if mask != 0 {
dvs := ubc.SHA1_dvs()
for i := 0; dvs[i].DvType != 0; i++ {
@@ -290,23 +277,22 @@ func rectifyCompressionState(
return
}
func3 := func(state [shared.WordBuffers]uint32, i int) [shared.WordBuffers]uint32 {
a, b, c, d, e := state[0], state[1], state[2], state[3], state[4]
// The words are loaded and stored one at a time. Passing [5]uint32 values
// around made the compiler reload them with wider moves than they were
// stored with, which stalled on store forwarding each time.
// Advance cs[1] from the state before step 56 to the one before step 58.
a, b, c, d, e := cs[1][0], cs[1][1], cs[1][2], cs[1][3], cs[1][4]
for i := 56; i < 58; i++ {
f := ((b | c) & d) | (b & c)
t := bits.RotateLeft32(a, 5) + f + e + m1[i] + shared.K2
a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d
return [shared.WordBuffers]uint32{a, b, c, d, e}
}
func4 := func(state [shared.WordBuffers]uint32, i int) [shared.WordBuffers]uint32 {
a, b, c, d, e := state[0], state[1], state[2], state[3], state[4]
f := b ^ c ^ d
t := bits.RotateLeft32(a, 5) + f + e + m1[i] + shared.K3
a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d
return [shared.WordBuffers]uint32{a, b, c, d, e}
}
cs[1][0], cs[1][1], cs[1][2], cs[1][3], cs[1][4] = a, b, c, d, e
cs57 := func3(cs[1], 56)
cs[1] = func3(cs57, 57)
cs[2] = func4(cs[2], 64)
// Advance cs[2] from the state before step 64 to the one before step 65.
a, b, c, d, e = cs[2][0], cs[2][1], cs[2][2], cs[2][3], cs[2][4]
f := b ^ c ^ d
t := bits.RotateLeft32(a, 5) + f + e + m1[64] + shared.K3
cs[2][0], cs[2][1], cs[2][2], cs[2][3], cs[2][4] = t, a, bits.RotateLeft32(b, 30), c, d
}
+51
View File
@@ -0,0 +1,51 @@
//go:build !noasm && gc && amd64
package ubc
import (
"unsafe"
"github.com/pjbgf/sha1cd/internal/cpu"
)
// useAVX512 reports whether calculateDvMaskAVX512 can run. Its only
// instructions outside AVX-512F are VMOVD and VZEROUPPER, which need AVX.
// HasAVX512F is set only when the CPU reports AVX as well.
var useAVX512 = cpu.X86.HasAVX512F
//go:noescape
func calculateDvMaskAVX512(W *[80]uint32, terms *uint32, groups int) uint32
// avx512Lanes is the number of terms a group of avx512Terms describes, and
// avx512Fields the vectors it holds for them.
const (
avx512Lanes = 16
avx512Fields = 6
)
// avx512Groups is how many passes of the kernel loop the table needs.
const avx512Groups = len(avx512Terms) / (avx512Fields * avx512Lanes)
// alignedTerms is a copy of avx512Terms that starts on a cache line. The
// linker aligns data to 32 bytes at most, and a 64 byte load that straddles
// two cache lines costs about twice as much.
var alignedTerms = func() *uint32 {
buf := make([]uint32, len(avx512Terms)+16)
off := (64 - uintptr(unsafe.Pointer(&buf[0]))%64) % 64 / 4
copy(buf[off:], avx512Terms[:])
return &buf[off]
}()
// CalculateDvMask takes as input an expanded message block and
// verifies the unavoidable bitconditions for all listed DVs. It returns
// a dvmask where each bit belonging to a DV is set if all unavoidable
// bitconditions for that DV have been met.
func CalculateDvMask(W *[80]uint32) uint32 {
if W == nil {
return 0
}
if useAVX512 {
return calculateDvMaskAVX512(W, alignedTerms, avx512Groups)
}
return calculateDvMaskGeneric(W)
}
+52
View File
@@ -0,0 +1,52 @@
//go:build !noasm && gc && amd64
#include "textflag.h"
// func calculateDvMaskAVX512(W *[80]uint32, terms *uint32, groups int) uint32
// Requires: AVX, AVX512F
//
// Each group of avx512Terms describes 16 of the bit tests of CalculateDvMask,
// as vectors of word indices, shift counts, expected parities and the masks
// that clear the DVs of a failing test. One pass of the loop runs a group.
//
// terms only has to be 64 byte aligned for speed, as otherwise every load of
// it straddles two cache lines. The word indices are biased by 34, matching
// the two vectors of W loaded below.
TEXT ·calculateDvMaskAVX512(SB), NOSPLIT, $0-28
MOVQ W+0(FP), AX
MOVQ terms+8(FP), BX
MOVQ groups+16(FP), CX
VMOVDQU32 136(AX), Z0 // W[34..49]
VMOVDQU32 200(AX), Z1 // W[50..65]
VPTERNLOGD $0xff, Z2, Z2, Z2 // mask = all ones
MOVL $1, DX
VPBROADCASTD DX, Z3
loop:
VMOVDQU32 0(BX), Z4
VMOVDQU32 64(BX), Z5
VPERMI2D Z1, Z0, Z4 // W[a]
VPERMI2D Z1, Z0, Z5 // W[b]
VPSRLVD 128(BX), Z4, Z4 // >> ka
VPSRLVD 192(BX), Z5, Z5 // >> kb
VPTERNLOGD $0x96, 256(BX), Z5, Z4 // ^ e
VPTESTMD Z3, Z4, K1 // the terms whose bit test fails
VPANDD 320(BX), Z2, K1, Z2 // clear their DVs
ADDQ $384, BX
DECQ CX
JNZ loop
// AND the 16 lanes together, first the 128 bit lanes and then the
// words within them.
VSHUFI64X2 $0x4e, Z2, Z2, Z4
VPANDD Z4, Z2, Z2
VSHUFI64X2 $0xb1, Z2, Z2, Z4
VPANDD Z4, Z2, Z2
VPSHUFD $0x4e, Z2, Z4
VPANDD Z4, Z2, Z2
VPSHUFD $0xb1, Z2, Z4
VPANDD Z4, Z2, Z2
VMOVD X2, AX
VZEROUPPER
MOVL AX, ret+24(FP)
RET
+111
View File
@@ -0,0 +1,111 @@
//go:build !noasm && gc && amd64
package ubc
// avx512Terms holds the bit tests of CalculateDvMask in the layout the
// AVX-512 kernel consumes, 16 terms per group. Each term says that a set of
// DVs survives only if bit ka of W[a] XOR bit kb of W[b] equals e, and names
// the mask that clears those DVs when it does not.
//
// The six vectors of a group are, in order: a-34, b-34, ka, kb, e, and the
// clear mask. Word indices are biased by 34 because the kernel keeps W[34..65]
// in two registers. A group is 384 bytes, and the kernel runs one per pass.
//
// The terms are those of calculateDvMaskGeneric, which is a port of
// ubc_check.c and has not changed since the collision detection was published
// in 2017. TestAVX512MatchesGeneric holds the two implementations to the same
// results over inputs that reach every DV, so a mistake here is a test
// failure rather than a missed collision. The comment above each group names
// its terms as a.ka^b.kb=e.
var avx512Terms = [960]uint32{
// terms 0-15
// 44.29^45.29=0 49.29^50.29=0 48.29^49.29=0 47.4^50.29=0 47.29^48.29=0 46.4^49.29=0 46.29^47.29=0 45.4^48.29=0
// 45.29^46.29=0 44.4^47.29=0 43.4^46.29=0 43.29^44.29=0 42.4^45.29=0 41.4^44.29=0 40.29^41.29=0 54.29^55.29=0
0x0000000a, 0x0000000f, 0x0000000e, 0x0000000d, 0x0000000d, 0x0000000c, 0x0000000c, 0x0000000b, 0x0000000b, 0x0000000a, 0x00000009, 0x00000009, 0x00000008, 0x00000007, 0x00000006, 0x00000014,
0x0000000b, 0x00000010, 0x0000000f, 0x00000010, 0x0000000e, 0x0000000f, 0x0000000d, 0x0000000e, 0x0000000c, 0x0000000d, 0x0000000c, 0x0000000a, 0x0000000b, 0x0000000a, 0x00000007, 0x00000015,
0x0000001d, 0x0000001d, 0x0000001d, 0x00000004, 0x0000001d, 0x00000004, 0x0000001d, 0x00000004, 0x0000001d, 0x00000004, 0x00000004, 0x0000001d, 0x00000004, 0x00000004, 0x0000001d, 0x0000001d,
0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0xfd7c5f7f, 0x3d7efff7, 0x9f5f7ffb, 0x7dfedddf, 0xcfcfdffd, 0xbf7f7777, 0xe7e7f7fe, 0xdfdfdddb, 0xf5f57dff, 0xefeff775, 0xf7f7fdda, 0xff5ed7df, 0xfdfd7f75, 0xff7edfda, 0x7ff5ff5d, 0x3f77dfff,
// terms 16-31
// 53.29^54.29=0 52.29^53.29=0 50.4^53.29=0 50.29^51.29=0 49.4^52.29=0 48.4^51.29=0 42.29^43.29=0 41.29^42.29=0
// 40.4^43.29=0 39.4^42.29=0 38.4^41.29=0 37.4^40.29=0 55.29^56.29=0 52.4^55.29=0 51.4^54.29=0 51.29^52.29=0
0x00000013, 0x00000012, 0x00000010, 0x00000010, 0x0000000f, 0x0000000e, 0x00000008, 0x00000007, 0x00000006, 0x00000005, 0x00000004, 0x00000003, 0x00000015, 0x00000012, 0x00000011, 0x00000011,
0x00000014, 0x00000013, 0x00000013, 0x00000011, 0x00000012, 0x00000011, 0x00000009, 0x00000008, 0x00000009, 0x00000008, 0x00000007, 0x00000006, 0x00000016, 0x00000015, 0x00000014, 0x00000012,
0x0000001d, 0x0000001d, 0x00000004, 0x0000001d, 0x00000004, 0x00000004, 0x0000001d, 0x0000001d, 0x00000004, 0x00000004, 0x00000004, 0x00000004, 0x0000001d, 0x00000004, 0x00000004, 0x0000001d,
0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x9fddf7ff, 0xcfeefdff, 0xdfed77ff, 0x75fdffdf, 0xeff6ddff, 0xf7fd777f, 0xffcff5f7, 0xffe7fd7b, 0x7fdff7f5, 0xbfeffdfa, 0x5ff7ff7d, 0xaffdffde, 0x7def7fff, 0x7f6f7fff, 0xbfd7dfff, 0xe7f7ff7f,
// terms 32-47
// 36.4^40.29=0 53.29^56.29=1 51.29^54.29=1 50.29^52.29=1 49.29^51.29=1 48.29^50.29=1 47.29^49.29=1 46.29^48.29=1
// 45.6^47.6=0 45.29^47.29=1 44.6^46.6=0 44.29^46.29=1 41.1^42.6=1 40.1^41.6=1 40.4^42.4=1 39.1^40.6=1
0x00000002, 0x00000013, 0x00000011, 0x00000010, 0x0000000f, 0x0000000e, 0x0000000d, 0x0000000c, 0x0000000b, 0x0000000b, 0x0000000a, 0x0000000a, 0x00000007, 0x00000006, 0x00000006, 0x00000005,
0x00000006, 0x00000016, 0x00000014, 0x00000012, 0x00000011, 0x00000010, 0x0000000f, 0x0000000e, 0x0000000d, 0x0000000d, 0x0000000c, 0x0000000c, 0x00000008, 0x00000007, 0x00000008, 0x00000006,
0x00000004, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x00000006, 0x0000001d, 0x00000006, 0x0000001d, 0x00000001, 0x00000001, 0x00000004, 0x00000001,
0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x00000006, 0x0000001d, 0x00000006, 0x0000001d, 0x00000006, 0x00000006, 0x00000004, 0x00000006,
0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
0xffeefdf7, 0xffcf7fff, 0xfff5f7ff, 0xfffeddff, 0xffff777f, 0xffffdddf, 0xfffff777, 0xfffffddb, 0xffffbbbf, 0xffffff75, 0xffffeeef, 0xffffffda, 0xfbfbfeff, 0xfeffbfbf, 0x7ffffff5, 0xffbfefef,
// terms 48-63
// 39.4^41.4=1 38.4^40.4=1 37.4^39.4=1 36.1^37.6=1 35.4^39.29=0 63.0^64.5=1 63.1^64.6=1 62.0^63.5=1
// 61.0^62.5=1 61.2^62.7=1 60.0^61.5=1 58.29^59.29=0 57.29^58.29=0 56.4^59.29=0 56.29^59.29=1 56.29^57.29=0
0x00000005, 0x00000004, 0x00000003, 0x00000002, 0x00000001, 0x0000001d, 0x0000001d, 0x0000001c, 0x0000001b, 0x0000001b, 0x0000001a, 0x00000018, 0x00000017, 0x00000016, 0x00000016, 0x00000016,
0x00000007, 0x00000006, 0x00000005, 0x00000003, 0x00000005, 0x0000001e, 0x0000001e, 0x0000001d, 0x0000001c, 0x0000001c, 0x0000001b, 0x00000019, 0x00000018, 0x00000019, 0x00000019, 0x00000017,
0x00000004, 0x00000004, 0x00000004, 0x00000001, 0x00000004, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000002, 0x00000000, 0x0000001d, 0x0000001d, 0x00000004, 0x0000001d, 0x0000001d,
0x00000004, 0x00000004, 0x00000004, 0x00000006, 0x0000001d, 0x00000005, 0x00000006, 0x00000005, 0x00000005, 0x00000007, 0x00000005, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d,
0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
0xbffffffa, 0x5ffffffd, 0xaffffffe, 0xfffbefbf, 0xfff7ff7b, 0xffefff7f, 0xfffefffb, 0xfff7ffdf, 0xfffdfff7, 0xfffbffef, 0xfffefffb, 0xddffffff, 0xef7fffff, 0xd7ffffff, 0xf5ffffff, 0xf7dfffff,
// terms 64-79
// 55.4^58.29=0 54.4^57.29=0 53.4^56.29=0 50.6^51.1=0 48.6^50.6=0 48.29^55.29=1 47.6^49.6=0 47.6^48.1=0
// 46.6^48.6=0 46.6^47.1=0 44.1^45.6=1 43.6^45.6=0 42.6^44.6=0 42.6^43.1=0 41.6^42.1=0 40.6^41.1=0
0x00000015, 0x00000014, 0x00000013, 0x00000010, 0x0000000e, 0x0000000e, 0x0000000d, 0x0000000d, 0x0000000c, 0x0000000c, 0x0000000a, 0x00000009, 0x00000008, 0x00000008, 0x00000007, 0x00000006,
0x00000018, 0x00000017, 0x00000016, 0x00000011, 0x00000010, 0x00000015, 0x0000000f, 0x0000000e, 0x0000000e, 0x0000000d, 0x0000000b, 0x0000000b, 0x0000000a, 0x00000009, 0x00000008, 0x00000007,
0x00000004, 0x00000004, 0x00000004, 0x00000006, 0x00000006, 0x0000001d, 0x00000006, 0x00000006, 0x00000006, 0x00000006, 0x00000001, 0x00000006, 0x00000006, 0x00000006, 0x00000006, 0x00000006,
0x0000001d, 0x0000001d, 0x0000001d, 0x00000001, 0x00000006, 0x0000001d, 0x00000006, 0x00000001, 0x00000006, 0x00000001, 0x00000006, 0x00000006, 0x00000006, 0x00000001, 0x00000001, 0x00000001,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0xedffffff, 0xf77fffff, 0xfddfffff, 0xfffbefff, 0xfffbefff, 0xffff5fff, 0xffffbbff, 0xfbffffbf, 0xffffeeff, 0xfeffffef, 0xffbfbfff, 0xfffffbbf, 0xfffffeef, 0xfbfbffff, 0xfeffbfff, 0xffbfefff,
// terms 80-95
// 39.4^43.29=0 38.4^42.29=0 37.1^38.6=1 37.4^41.29=0 36.4^38.4=1 35.1^36.6=1 35.3^39.28=0 61.1^62.6=1
// 59.5^63.30=0 58.0^63.30=1 62.1^63.6=1 60.5^64.30=0 59.0^64.30=1 40.6^42.6=0 62.2^63.7=1 41.6^43.6=0
0x00000005, 0x00000004, 0x00000003, 0x00000003, 0x00000002, 0x00000001, 0x00000001, 0x0000001b, 0x00000019, 0x00000018, 0x0000001c, 0x0000001a, 0x00000019, 0x00000006, 0x0000001c, 0x00000007,
0x00000009, 0x00000008, 0x00000004, 0x00000007, 0x00000004, 0x00000002, 0x00000005, 0x0000001c, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001e, 0x0000001e, 0x00000008, 0x0000001d, 0x00000009,
0x00000004, 0x00000004, 0x00000001, 0x00000004, 0x00000004, 0x00000001, 0x00000003, 0x00000001, 0x00000005, 0x00000000, 0x00000001, 0x00000005, 0x00000000, 0x00000006, 0x00000002, 0x00000006,
0x0000001d, 0x0000001d, 0x00000006, 0x0000001d, 0x00000004, 0x00000006, 0x0000001c, 0x00000006, 0x0000001e, 0x0000001e, 0x00000006, 0x0000001e, 0x0000001e, 0x00000006, 0x00000007, 0x00000006,
0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0x00000000, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0x00000000, 0x00000001, 0x00000000,
0xfdff7fff, 0xff7fdfff, 0xffffbeff, 0xffdff7ff, 0xd7ffffff, 0xfffffbef, 0xfff7dfff, 0xfffffffe, 0xfffffffe, 0xfffffffe, 0xfffffffd, 0xfffffffd, 0xfffffffd, 0xffffffef, 0xffffffbf, 0xffffffbf,
// terms 96-111
// 63.2^64.7=1 48.6^49.1=0 49.6^50.1=0 42.1^50.1=1 39.6^40.1=0 38.1^40.1=1 36.4^37.4=1 43.1^51.1=1
// 37.4^38.4=1 51.6^52.1=0 49.6^51.6=0 37.1^37.6=0 35.5^39.30=0 38.4^39.4=1 47.1^51.1=1 36.3^40.28=0
0x0000001d, 0x0000000e, 0x0000000f, 0x00000008, 0x00000005, 0x00000004, 0x00000002, 0x00000009, 0x00000003, 0x00000011, 0x0000000f, 0x00000003, 0x00000001, 0x00000004, 0x0000000d, 0x00000002,
0x0000001e, 0x0000000f, 0x00000010, 0x00000010, 0x00000006, 0x00000006, 0x00000003, 0x00000011, 0x00000004, 0x00000012, 0x00000011, 0x00000003, 0x00000005, 0x00000005, 0x00000011, 0x00000006,
0x00000002, 0x00000006, 0x00000006, 0x00000001, 0x00000006, 0x00000001, 0x00000004, 0x00000001, 0x00000004, 0x00000006, 0x00000006, 0x00000001, 0x00000005, 0x00000004, 0x00000001, 0x00000003,
0x00000007, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000004, 0x00000001, 0x00000004, 0x00000001, 0x00000006, 0x00000006, 0x0000001e, 0x00000004, 0x00000001, 0x0000001c,
0x00000001, 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000000,
0xfffffeff, 0xfffffeff, 0xfffffbff, 0xfffffbff, 0xfffffbff, 0xfffffbff, 0xfffff7ff, 0xffffefff, 0xffffdfff, 0xffffbfff, 0xffffbfff, 0xffffbfff, 0xffffbfff, 0xffff7fff, 0xfffbffff, 0xffefffff,
// terms 112-127
// 35.30^40.28=1 37.3^41.28=0 36.30^41.28=1 53.6^54.1=0 51.6^53.6=0 50.1^54.1=1 45.6^46.1=0 37.5^41.30=0
// 36.0^41.30=1 55.29^58.29=1 38.3^42.28=0 37.30^42.28=1 54.6^55.1=0 52.6^54.6=0 51.1^55.1=1 45.1^47.1=1
0x00000001, 0x00000003, 0x00000002, 0x00000013, 0x00000011, 0x00000010, 0x0000000b, 0x00000003, 0x00000002, 0x00000015, 0x00000004, 0x00000003, 0x00000014, 0x00000012, 0x00000011, 0x0000000b,
0x00000006, 0x00000007, 0x00000007, 0x00000014, 0x00000013, 0x00000014, 0x0000000c, 0x00000007, 0x00000007, 0x00000018, 0x00000008, 0x00000008, 0x00000015, 0x00000014, 0x00000015, 0x0000000d,
0x0000001e, 0x00000003, 0x0000001e, 0x00000006, 0x00000006, 0x00000001, 0x00000006, 0x00000005, 0x00000000, 0x0000001d, 0x00000003, 0x0000001e, 0x00000006, 0x00000006, 0x00000001, 0x00000001,
0x0000001c, 0x0000001c, 0x0000001c, 0x00000001, 0x00000006, 0x00000001, 0x00000001, 0x0000001e, 0x0000001e, 0x0000001d, 0x0000001c, 0x0000001c, 0x00000001, 0x00000006, 0x00000001, 0x00000001,
0x00000001, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000001, 0x00000001,
0xffefffff, 0xffdfffff, 0xffdfffff, 0xffbfffff, 0xffbfffff, 0xffbfffff, 0xffbfffff, 0xffbfffff, 0xffbfffff, 0xff7fffff, 0xff7fffff, 0xff7fffff, 0xfeffffff, 0xfeffffff, 0xfeffffff, 0xfeffffff,
// terms 128-143
// 38.5^42.30=0 37.0^42.30=1 39.3^43.28=0 38.30^43.28=1 55.6^56.1=0 53.6^55.6=0 52.1^56.1=1 46.1^48.1=1
// 39.5^43.30=0 38.0^43.30=1 59.29^60.29=0 40.3^44.28=0 40.4^44.29=0 39.30^44.28=1 58.29^61.29=1 57.4^61.29=0
0x00000004, 0x00000003, 0x00000005, 0x00000004, 0x00000015, 0x00000013, 0x00000012, 0x0000000c, 0x00000005, 0x00000004, 0x00000019, 0x00000006, 0x00000006, 0x00000005, 0x00000018, 0x00000017,
0x00000008, 0x00000008, 0x00000009, 0x00000009, 0x00000016, 0x00000015, 0x00000016, 0x0000000e, 0x00000009, 0x00000009, 0x0000001a, 0x0000000a, 0x0000000a, 0x0000000a, 0x0000001b, 0x0000001b,
0x00000005, 0x00000000, 0x00000003, 0x0000001e, 0x00000006, 0x00000006, 0x00000001, 0x00000001, 0x00000005, 0x00000000, 0x0000001d, 0x00000003, 0x00000004, 0x0000001e, 0x0000001d, 0x00000004,
0x0000001e, 0x0000001e, 0x0000001c, 0x0000001c, 0x00000001, 0x00000006, 0x00000001, 0x00000001, 0x0000001e, 0x0000001e, 0x0000001d, 0x0000001c, 0x0000001d, 0x0000001c, 0x0000001d, 0x0000001d,
0x00000000, 0x00000001, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000000,
0xfeffffff, 0xfeffffff, 0xfdffffff, 0xfdffffff, 0xfbffffff, 0xfbffffff, 0xfbffffff, 0xfbffffff, 0xfbffffff, 0xfbffffff, 0xf7ffffff, 0xf7ffffff, 0xf7ffffff, 0xf7ffffff, 0xefffffff, 0xefffffff,
// terms 144-159
// 41.3^45.28=0 41.4^45.29=0 58.4^62.29=0 42.3^46.28=0 42.4^46.29=0 59.4^63.29=0 57.4^59.29=0 43.3^47.28=0
// 43.4^47.29=0 60.4^64.29=0 44.3^48.28=0 44.4^48.29=0 35.0^35.0=0 35.0^35.0=0 35.0^35.0=0 35.0^35.0=0
0x00000007, 0x00000007, 0x00000018, 0x00000008, 0x00000008, 0x00000019, 0x00000017, 0x00000009, 0x00000009, 0x0000001a, 0x0000000a, 0x0000000a, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
0x0000000b, 0x0000000b, 0x0000001c, 0x0000000c, 0x0000000c, 0x0000001d, 0x00000019, 0x0000000d, 0x0000000d, 0x0000001e, 0x0000000e, 0x0000000e, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
0x00000003, 0x00000004, 0x00000004, 0x00000003, 0x00000004, 0x00000004, 0x00000004, 0x00000003, 0x00000004, 0x00000004, 0x00000003, 0x00000004, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x0000001c, 0x0000001d, 0x0000001d, 0x0000001c, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001c, 0x0000001d, 0x0000001d, 0x0000001c, 0x0000001d, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0xefffffff, 0xefffffff, 0xdfffffff, 0xdfffffff, 0xdfffffff, 0xbfffffff, 0xbfffffff, 0xbfffffff, 0xbfffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
}
+14
View File
@@ -0,0 +1,14 @@
//go:build noasm || !gc || !amd64
package ubc
// CalculateDvMask takes as input an expanded message block and
// verifies the unavoidable bitconditions for all listed DVs. It returns
// a dvmask where each bit belonging to a DV is set if all unavoidable
// bitconditions for that DV have been met.
func CalculateDvMask(W *[80]uint32) uint32 {
if W == nil {
return 0
}
return calculateDvMaskGeneric(W)
}
+4 -8
View File
@@ -23,16 +23,12 @@ type DvInfo struct {
Dm [80]uint32
}
// CalculateDvMask takes as input an expanded message block and
// verifies the unavoidable bitconditions for all listed DVs. It returns
// a dvmask where each bit belonging to a DV is set if all unavoidable
// bitconditions for that DV have been met.
// calculateDvMaskGeneric is the portable implementation of CalculateDvMask.
// The AVX-512 kernel runs the same bit tests from avx512Terms, and
// TestAVX512MatchesGeneric holds the two to the same results.
//
//go:nosplit
func CalculateDvMask(W *[80]uint32) uint32 {
if W == nil {
return 0
}
func calculateDvMaskGeneric(W *[80]uint32) uint32 {
mask := uint32(0xFFFFFFFF)
mask &= (((((W[44] ^ W[45]) >> 29) & 1) - 1) | ^(DV_I_48_0_bit | DV_I_51_0_bit | DV_I_52_0_bit | DV_II_45_0_bit | DV_II_46_0_bit | DV_II_50_0_bit | DV_II_51_0_bit))
mask &= (((((W[49] ^ W[50]) >> 29) & 1) - 1) | ^(DV_I_46_0_bit | DV_II_45_0_bit | DV_II_50_0_bit | DV_II_51_0_bit | DV_II_55_0_bit | DV_II_56_0_bit))
+20 -8
View File
@@ -84,7 +84,7 @@ func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, ar
return Equal(t, expected, actual, append([]interface{}{msg}, args...)...)
}
// EqualErrorf asserts that a function returned an error (i.e. not `nil`)
// EqualErrorf asserts that a function returned a non-nil error (i.e. an error)
// and that it is equal to the provided error.
//
// actualObj, err := SomeFunction()
@@ -124,7 +124,7 @@ func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg stri
return EqualValues(t, expected, actual, append([]interface{}{msg}, args...)...)
}
// Errorf asserts that a function returned an error (i.e. not `nil`).
// Errorf asserts that a function returned a non-nil error (ie. an error).
//
// actualObj, err := SomeFunction()
// assert.Errorf(t, err, "error message %s", "formatted")
@@ -144,8 +144,8 @@ func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...int
return ErrorAs(t, err, target, append([]interface{}{msg}, args...)...)
}
// ErrorContainsf asserts that a function returned an error (i.e. not `nil`)
// and that the error contains the specified substring.
// ErrorContainsf asserts that a function returned a non-nil error (i.e. an
// error) and that the error contains the specified substring.
//
// actualObj, err := SomeFunction()
// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted")
@@ -190,10 +190,10 @@ func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick
// time.Sleep(8*time.Second)
// externalValue = true
// }()
// assert.EventuallyWithTf(t, func(c *assert.CollectT, "error message %s", "formatted") {
// assert.EventuallyWithTf(t, func(c *assert.CollectT) {
// // add assertions as needed; any assertion failure will fail the current tick
// assert.True(c, externalValue, "expected 'externalValue' to be true")
// }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false")
// }, 10*time.Second, 1*time.Second, "error message %s", "formatted")
func EventuallyWithTf(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
@@ -552,7 +552,7 @@ func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) bool
return NoDirExists(t, path, append([]interface{}{msg}, args...)...)
}
// NoErrorf asserts that a function returned no error (i.e. `nil`).
// NoErrorf asserts that a function returned a nil error (ie. no error).
//
// actualObj, err := SomeFunction()
// if assert.NoErrorf(t, err, "error message %s", "formatted") {
@@ -849,7 +849,19 @@ func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time,
return WithinRange(t, actual, start, end, append([]interface{}{msg}, args...)...)
}
// YAMLEqf asserts that two YAML strings are equivalent.
// YAMLEqf asserts that the first documents in the two YAML strings are equivalent.
//
// expected := `---
// key: value
// ---
// key: this is a second document, it is not evaluated
// `
// actual := `---
// key: value
// ---
// key: this is a subsequent document, it is not evaluated
// `
// assert.YAMLEqf(t, expected, actual, "error message %s", "formatted")
func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
+38 -14
View File
@@ -146,7 +146,7 @@ func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs
return Equal(a.t, expected, actual, msgAndArgs...)
}
// EqualError asserts that a function returned an error (i.e. not `nil`)
// EqualError asserts that a function returned a non-nil error (i.e. an error)
// and that it is equal to the provided error.
//
// actualObj, err := SomeFunction()
@@ -158,7 +158,7 @@ func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...
return EqualError(a.t, theError, errString, msgAndArgs...)
}
// EqualErrorf asserts that a function returned an error (i.e. not `nil`)
// EqualErrorf asserts that a function returned a non-nil error (i.e. an error)
// and that it is equal to the provided error.
//
// actualObj, err := SomeFunction()
@@ -240,7 +240,7 @@ func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string
return Equalf(a.t, expected, actual, msg, args...)
}
// Error asserts that a function returned an error (i.e. not `nil`).
// Error asserts that a function returned a non-nil error (ie. an error).
//
// actualObj, err := SomeFunction()
// a.Error(err)
@@ -269,8 +269,8 @@ func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args ..
return ErrorAsf(a.t, err, target, msg, args...)
}
// ErrorContains asserts that a function returned an error (i.e. not `nil`)
// and that the error contains the specified substring.
// ErrorContains asserts that a function returned a non-nil error (i.e. an
// error) and that the error contains the specified substring.
//
// actualObj, err := SomeFunction()
// a.ErrorContains(err, expectedErrorSubString)
@@ -281,8 +281,8 @@ func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs .
return ErrorContains(a.t, theError, contains, msgAndArgs...)
}
// ErrorContainsf asserts that a function returned an error (i.e. not `nil`)
// and that the error contains the specified substring.
// ErrorContainsf asserts that a function returned a non-nil error (i.e. an
// error) and that the error contains the specified substring.
//
// actualObj, err := SomeFunction()
// a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted")
@@ -311,7 +311,7 @@ func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...inter
return ErrorIsf(a.t, err, target, msg, args...)
}
// Errorf asserts that a function returned an error (i.e. not `nil`).
// Errorf asserts that a function returned a non-nil error (ie. an error).
//
// actualObj, err := SomeFunction()
// a.Errorf(err, "error message %s", "formatted")
@@ -372,10 +372,10 @@ func (a *Assertions) EventuallyWithT(condition func(collect *CollectT), waitFor
// time.Sleep(8*time.Second)
// externalValue = true
// }()
// a.EventuallyWithTf(func(c *assert.CollectT, "error message %s", "formatted") {
// a.EventuallyWithTf(func(c *assert.CollectT) {
// // add assertions as needed; any assertion failure will fail the current tick
// assert.True(c, externalValue, "expected 'externalValue' to be true")
// }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false")
// }, 10*time.Second, 1*time.Second, "error message %s", "formatted")
func (a *Assertions) EventuallyWithTf(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
@@ -1096,7 +1096,7 @@ func (a *Assertions) NoDirExistsf(path string, msg string, args ...interface{})
return NoDirExistsf(a.t, path, msg, args...)
}
// NoError asserts that a function returned no error (i.e. `nil`).
// NoError asserts that a function returned a nil error (ie. no error).
//
// actualObj, err := SomeFunction()
// if a.NoError(err) {
@@ -1109,7 +1109,7 @@ func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool {
return NoError(a.t, err, msgAndArgs...)
}
// NoErrorf asserts that a function returned no error (i.e. `nil`).
// NoErrorf asserts that a function returned a nil error (ie. no error).
//
// actualObj, err := SomeFunction()
// if a.NoErrorf(err, "error message %s", "formatted") {
@@ -1690,7 +1690,19 @@ func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Ti
return WithinRangef(a.t, actual, start, end, msg, args...)
}
// YAMLEq asserts that two YAML strings are equivalent.
// YAMLEq asserts that the first documents in the two YAML strings are equivalent.
//
// expected := `---
// key: value
// ---
// key: this is a second document, it is not evaluated
// `
// actual := `---
// key: value
// ---
// key: this is a subsequent document, it is not evaluated
// `
// a.YAMLEq(expected, actual)
func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
@@ -1698,7 +1710,19 @@ func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interf
return YAMLEq(a.t, expected, actual, msgAndArgs...)
}
// YAMLEqf asserts that two YAML strings are equivalent.
// YAMLEqf asserts that the first documents in the two YAML strings are equivalent.
//
// expected := `---
// key: value
// ---
// key: this is a second document, it is not evaluated
// `
// actual := `---
// key: value
// ---
// key: this is a subsequent document, it is not evaluated
// `
// a.YAMLEqf(expected, actual, "error message %s", "formatted")
func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
+13 -1
View File
@@ -9,7 +9,7 @@ import (
func isOrdered(t TestingT, object interface{}, allowedComparesResults []compareResult, failMessage string, msgAndArgs ...interface{}) bool {
objKind := reflect.TypeOf(object).Kind()
if objKind != reflect.Slice && objKind != reflect.Array {
return false
return Fail(t, fmt.Sprintf("object %T is not an ordered collection", object), msgAndArgs...)
}
objValue := reflect.ValueOf(object)
@@ -50,6 +50,9 @@ func isOrdered(t TestingT, object interface{}, allowedComparesResults []compareR
// assert.IsIncreasing(t, []float{1, 2})
// assert.IsIncreasing(t, []string{"a", "b"})
func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return isOrdered(t, object, []compareResult{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...)
}
@@ -59,6 +62,9 @@ func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) boo
// assert.IsNonIncreasing(t, []float{2, 1})
// assert.IsNonIncreasing(t, []string{"b", "a"})
func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return isOrdered(t, object, []compareResult{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...)
}
@@ -68,6 +74,9 @@ func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{})
// assert.IsDecreasing(t, []float{2, 1})
// assert.IsDecreasing(t, []string{"b", "a"})
func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return isOrdered(t, object, []compareResult{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...)
}
@@ -77,5 +86,8 @@ func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) boo
// assert.IsNonDecreasing(t, []float{1, 2})
// assert.IsNonDecreasing(t, []string{"a", "b"})
func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return isOrdered(t, object, []compareResult{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...)
}
+83 -64
View File
@@ -17,11 +17,10 @@ import (
"unicode"
"unicode/utf8"
"github.com/davecgh/go-spew/spew"
"github.com/pmezard/go-difflib/difflib"
// Wrapper around gopkg.in/yaml.v3
// Wrapper around go.yaml.in/yaml/v3
"github.com/stretchr/testify/assert/yaml"
"github.com/stretchr/testify/internal/difflib"
"github.com/stretchr/testify/internal/spew"
)
//go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_format.go.tmpl"
@@ -33,19 +32,19 @@ type TestingT interface {
// ComparisonAssertionFunc is a common function prototype when comparing two values. Can be useful
// for table driven tests.
type ComparisonAssertionFunc func(TestingT, interface{}, interface{}, ...interface{}) bool
type ComparisonAssertionFunc = func(TestingT, interface{}, interface{}, ...interface{}) bool
// ValueAssertionFunc is a common function prototype when validating a single value. Can be useful
// for table driven tests.
type ValueAssertionFunc func(TestingT, interface{}, ...interface{}) bool
type ValueAssertionFunc = func(TestingT, interface{}, ...interface{}) bool
// BoolAssertionFunc is a common function prototype when validating a bool value. Can be useful
// for table driven tests.
type BoolAssertionFunc func(TestingT, bool, ...interface{}) bool
type BoolAssertionFunc = func(TestingT, bool, ...interface{}) bool
// ErrorAssertionFunc is a common function prototype when validating an error value. Can be useful
// for table driven tests.
type ErrorAssertionFunc func(TestingT, error, ...interface{}) bool
type ErrorAssertionFunc = func(TestingT, error, ...interface{}) bool
// PanicAssertionFunc is a common function prototype when validating a panic value. Can be useful
// for table driven tests.
@@ -325,13 +324,15 @@ func messageFromMsgAndArgs(msgAndArgs ...interface{}) string {
func indentMessageLines(message string, longestLabelLen int) string {
outBuf := new(bytes.Buffer)
for i, scanner := 0, bufio.NewScanner(strings.NewReader(message)); scanner.Scan(); i++ {
// no need to align first line because it starts at the correct location (after the label)
if i != 0 {
// append alignLen+1 spaces to align with "{{longestLabel}}:" before adding tab
outBuf.WriteString("\n\t" + strings.Repeat(" ", longestLabelLen+1) + "\t")
scanner := bufio.NewScanner(strings.NewReader(message))
for firstLine := true; scanner.Scan(); firstLine = false {
if !firstLine {
fmt.Fprint(outBuf, "\n\t"+strings.Repeat(" ", longestLabelLen+1)+"\t")
}
outBuf.WriteString(scanner.Text())
fmt.Fprint(outBuf, scanner.Text())
}
if err := scanner.Err(); err != nil {
return fmt.Sprintf("cannot display message: %s", err)
}
return outBuf.String()
@@ -544,9 +545,8 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b
if !same {
// both are pointers but not the same type & pointing to the same address
return Fail(t, fmt.Sprintf("Not same: \n"+
"expected: %p %#[1]v\n"+
"actual : %p %#[2]v",
expected, actual), msgAndArgs...)
"expected: %[2]s (%[1]T)(%[1]p)\n"+
"actual : %[4]s (%[3]T)(%[3]p)", expected, truncatingFormat("%#v", expected), actual, truncatingFormat("%#v", actual)), msgAndArgs...)
}
return true
@@ -571,8 +571,8 @@ func NotSame(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}
if same {
return Fail(t, fmt.Sprintf(
"Expected and actual point to the same object: %p %#[1]v",
expected), msgAndArgs...)
"Expected and actual point to the same object: %p %s",
expected, truncatingFormat("%#v", expected)), msgAndArgs...)
}
return true
}
@@ -604,25 +604,26 @@ func samePointers(first, second interface{}) (same bool, ok bool) {
// to a type conversion in the Go grammar.
func formatUnequalValues(expected, actual interface{}) (e string, a string) {
if reflect.TypeOf(expected) != reflect.TypeOf(actual) {
return fmt.Sprintf("%T(%s)", expected, truncatingFormat(expected)),
fmt.Sprintf("%T(%s)", actual, truncatingFormat(actual))
return fmt.Sprintf("%T(%s)", expected, truncatingFormat("%#v", expected)),
fmt.Sprintf("%T(%s)", actual, truncatingFormat("%#v", actual))
}
switch expected.(type) {
case time.Duration:
return fmt.Sprintf("%v", expected), fmt.Sprintf("%v", actual)
}
return truncatingFormat(expected), truncatingFormat(actual)
return truncatingFormat("%#v", expected), truncatingFormat("%#v", actual)
}
// truncatingFormat formats the data and truncates it if it's too long.
//
// This helps keep formatted error messages lines from exceeding the
// bufio.MaxScanTokenSize max line length that the go testing framework imposes.
func truncatingFormat(data interface{}) string {
value := fmt.Sprintf("%#v", data)
max := bufio.MaxScanTokenSize - 100 // Give us some space the type info too if needed.
if len(value) > max {
value = value[0:max] + "<... truncated>"
func truncatingFormat(format string, data interface{}) string {
value := fmt.Sprintf(format, data)
// Give us space for two truncated objects and the surrounding sentence.
maxMessageSize := bufio.MaxScanTokenSize/2 - 100
if len(value) > maxMessageSize {
value = value[0:maxMessageSize] + "<... truncated>"
}
return value
}
@@ -743,7 +744,7 @@ func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Fail(t, fmt.Sprintf("Expected nil, but got: %#v", object), msgAndArgs...)
return Fail(t, fmt.Sprintf("Expected nil, but got: %s", truncatingFormat("%#v", object)), msgAndArgs...)
}
// isEmpty gets whether the specified object is considered empty or not.
@@ -793,7 +794,7 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
Fail(t, fmt.Sprintf("Should be empty, but was %v", object), msgAndArgs...)
Fail(t, fmt.Sprintf("Should be empty, but was %s", truncatingFormat("%v", object)), msgAndArgs...)
}
return pass
@@ -836,11 +837,11 @@ func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{})
}
l, ok := getLen(object)
if !ok {
return Fail(t, fmt.Sprintf("\"%v\" could not be applied builtin len()", object), msgAndArgs...)
return Fail(t, fmt.Sprintf("%q could not be applied builtin len()", truncatingFormat("%v", object)), msgAndArgs...)
}
if l != length {
return Fail(t, fmt.Sprintf("\"%v\" should have %d item(s), but has %d", object, length, l), msgAndArgs...)
return Fail(t, fmt.Sprintf("%q should have %d item(s), but has %d", truncatingFormat("%v", object), length, l), msgAndArgs...)
}
return true
}
@@ -889,7 +890,7 @@ func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{
}
if ObjectsAreEqual(expected, actual) {
return Fail(t, fmt.Sprintf("Should not be: %#v\n", actual), msgAndArgs...)
return Fail(t, fmt.Sprintf("Should not be: %s\n", truncatingFormat("%#v", actual)), msgAndArgs...)
}
return true
@@ -904,7 +905,7 @@ func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...inte
}
if ObjectsAreEqualValues(expected, actual) {
return Fail(t, fmt.Sprintf("Should not be: %#v\n", actual), msgAndArgs...)
return Fail(t, fmt.Sprintf("Should not be: %s\n", truncatingFormat("%#v", actual)), msgAndArgs...)
}
return true
@@ -964,10 +965,10 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo
ok, found := containsElement(s, contains)
if !ok {
return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...)
return Fail(t, fmt.Sprintf("%s could not be applied builtin len()", truncatingFormat("%#v", s)), msgAndArgs...)
}
if !found {
return Fail(t, fmt.Sprintf("%#v does not contain %#v", s, contains), msgAndArgs...)
return Fail(t, fmt.Sprintf("%s does not contain %#v", truncatingFormat("%#v", s), contains), msgAndArgs...)
}
return true
@@ -986,10 +987,10 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{})
ok, found := containsElement(s, contains)
if !ok {
return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...)
return Fail(t, fmt.Sprintf("%s could not be applied builtin len()", truncatingFormat("%#v", s)), msgAndArgs...)
}
if found {
return Fail(t, fmt.Sprintf("%#v should not contain %#v", s, contains), msgAndArgs...)
return Fail(t, fmt.Sprintf("%s should not contain %#v", truncatingFormat("%#v", s), contains), msgAndArgs...)
}
return true
@@ -1031,10 +1032,10 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok
av := actualMap.MapIndex(k)
if !av.IsValid() {
return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...)
return Fail(t, fmt.Sprintf("%s does not contain %s", truncatingFormat("%#v", list), truncatingFormat("%#v", subset)), msgAndArgs...)
}
if !ObjectsAreEqual(ev.Interface(), av.Interface()) {
return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...)
return Fail(t, fmt.Sprintf("%s does not contain %s", truncatingFormat("%#v", list), truncatingFormat("%#v", subset)), msgAndArgs...)
}
}
@@ -1056,7 +1057,7 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok
return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", list), msgAndArgs...)
}
if !found {
return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, element), msgAndArgs...)
return Fail(t, fmt.Sprintf("%s does not contain %#v", truncatingFormat("%#v", list), element), msgAndArgs...)
}
}
@@ -1082,12 +1083,12 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{})
listKind := reflect.TypeOf(list).Kind()
if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map {
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...)
return Fail(t, fmt.Sprintf("%#v has an unsupported type %s", list, listKind), msgAndArgs...)
}
subsetKind := reflect.TypeOf(subset).Kind()
if subsetKind != reflect.Array && subsetKind != reflect.Slice && subsetKind != reflect.Map {
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...)
return Fail(t, fmt.Sprintf("%#v has an unsupported type %s", subset, subsetKind), msgAndArgs...)
}
if subsetKind == reflect.Map && listKind == reflect.Map {
@@ -1106,7 +1107,7 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{})
}
}
return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...)
return Fail(t, fmt.Sprintf("%s is a subset of %s", truncatingFormat("%#v", subset), truncatingFormat("%#v", list)), msgAndArgs...)
}
subsetList := reflect.ValueOf(subset)
@@ -1121,14 +1122,14 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{})
element := subsetList.Index(i).Interface()
ok, found := containsElement(list, element)
if !ok {
return Fail(t, fmt.Sprintf("%q could not be applied builtin len()", list), msgAndArgs...)
return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", list), msgAndArgs...)
}
if !found {
return true
}
}
return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...)
return Fail(t, fmt.Sprintf("%s is a subset of %s", truncatingFormat("%#v", subset), truncatingFormat("%#v", list)), msgAndArgs...)
}
// ElementsMatch asserts that the specified listA(array, slice...) is equal to specified
@@ -1343,9 +1344,15 @@ func PanicsWithError(t TestingT, errString string, f PanicTestFunc, msgAndArgs .
if !funcDidPanic {
return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...)
}
panicErr, ok := panicValue.(error)
if !ok || panicErr.Error() != errString {
return Fail(t, fmt.Sprintf("func %#v should panic with error message:\t%#v\n\tPanic value:\t%#v\n\tPanic stack:\t%s", f, errString, panicValue, panickedStack), msgAndArgs...)
panicErr, isError := panicValue.(error)
if !isError || panicErr.Error() != errString {
msg := fmt.Sprintf("func %#v should panic with error message:\t%#v\n", f, errString)
if isError {
msg += fmt.Sprintf("\tError message:\t%#v\n", panicErr.Error())
}
msg += fmt.Sprintf("\tPanic value:\t%#v\n", panicValue)
msg += fmt.Sprintf("\tPanic stack:\t%s\n", panickedStack)
return Fail(t, msg, msgAndArgs...)
}
return true
@@ -1624,7 +1631,7 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m
Errors
*/
// NoError asserts that a function returned no error (i.e. `nil`).
// NoError asserts that a function returned a nil error (ie. no error).
//
// actualObj, err := SomeFunction()
// if assert.NoError(t, err) {
@@ -1635,13 +1642,13 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Fail(t, fmt.Sprintf("Received unexpected error:\n%+v", err), msgAndArgs...)
return Fail(t, fmt.Sprintf("Received unexpected error:\n%s", truncatingFormat("%+v", err)), msgAndArgs...)
}
return true
}
// Error asserts that a function returned an error (i.e. not `nil`).
// Error asserts that a function returned a non-nil error (ie. an error).
//
// actualObj, err := SomeFunction()
// assert.Error(t, err)
@@ -1656,7 +1663,7 @@ func Error(t TestingT, err error, msgAndArgs ...interface{}) bool {
return true
}
// EqualError asserts that a function returned an error (i.e. not `nil`)
// EqualError asserts that a function returned a non-nil error (i.e. an error)
// and that it is equal to the provided error.
//
// actualObj, err := SomeFunction()
@@ -1674,13 +1681,13 @@ func EqualError(t TestingT, theError error, errString string, msgAndArgs ...inte
if expected != actual {
return Fail(t, fmt.Sprintf("Error message not equal:\n"+
"expected: %q\n"+
"actual : %q", expected, actual), msgAndArgs...)
"actual : %s", expected, truncatingFormat("%q", actual)), msgAndArgs...)
}
return true
}
// ErrorContains asserts that a function returned an error (i.e. not `nil`)
// and that the error contains the specified substring.
// ErrorContains asserts that a function returned a non-nil error (i.e. an
// error) and that the error contains the specified substring.
//
// actualObj, err := SomeFunction()
// assert.ErrorContains(t, err, expectedErrorSubString)
@@ -1694,7 +1701,7 @@ func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...in
actual := theError.Error()
if !strings.Contains(actual, contains) {
return Fail(t, fmt.Sprintf("Error %#v does not contain %#v", actual, contains), msgAndArgs...)
return Fail(t, fmt.Sprintf("Error %s does not contain %#v", truncatingFormat("%#v", actual), contains), msgAndArgs...)
}
return true
@@ -1760,7 +1767,7 @@ func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool {
h.Helper()
}
if i != nil && !reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) {
return Fail(t, fmt.Sprintf("Should be zero, but was %v", i), msgAndArgs...)
return Fail(t, fmt.Sprintf("Should be zero, but was %s", truncatingFormat("%v", i)), msgAndArgs...)
}
return true
}
@@ -1874,7 +1881,19 @@ func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{
return Equal(t, expectedJSONAsInterface, actualJSONAsInterface, msgAndArgs...)
}
// YAMLEq asserts that two YAML strings are equivalent.
// YAMLEq asserts that the first documents in the two YAML strings are equivalent.
//
// expected := `---
// key: value
// ---
// key: this is a second document, it is not evaluated
// `
// actual := `---
// key: value
// ---
// key: this is a subsequent document, it is not evaluated
// `
// assert.YAMLEq(t, expected, actual)
func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
@@ -2188,8 +2207,8 @@ func ErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool {
chain := buildErrorChainString(err, false)
return Fail(t, fmt.Sprintf("Target error should be in err chain:\n"+
"expected: %q\n"+
"in chain: %s", expectedText, chain,
"expected: %s\n"+
"in chain: %s", truncatingFormat("%q", expectedText), truncatingFormat("%s", chain),
), msgAndArgs...)
}
@@ -2211,8 +2230,8 @@ func NotErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool {
chain := buildErrorChainString(err, false)
return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+
"found: %q\n"+
"in chain: %s", expectedText, chain,
"found: %s\n"+
"in chain: %s", truncatingFormat("%q", expectedText), truncatingFormat("%s", chain),
), msgAndArgs...)
}
@@ -2236,7 +2255,7 @@ func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{
return Fail(t, fmt.Sprintf("Should be in error chain:\n"+
"expected: %s\n"+
"in chain: %s", expectedType, chain,
"in chain: %s", expectedType, truncatingFormat("%s", chain),
), msgAndArgs...)
}
@@ -2254,7 +2273,7 @@ func NotErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interfa
return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+
"found: %s\n"+
"in chain: %s", reflect.TypeOf(target).Elem().String(), chain,
"in chain: %s", reflect.TypeOf(target).Elem().String(), truncatingFormat("%s", chain),
), msgAndArgs...)
}
+2 -2
View File
@@ -40,8 +40,8 @@
//
// # Assertions
//
// Assertions allow you to easily write test code, and are global funcs in the `assert` package.
// All assertion functions take, as the first argument, the `*testing.T` object provided by the
// Assertions allow you to easily write test code, and are global funcs in the assert package.
// All assertion functions take, as the first argument, the [*testing.T] object provided by the
// testing framework. This allows the assertion funcs to write the failings and other details to
// the correct place.
//
+1 -1
View File
@@ -7,7 +7,7 @@
// go test -tags testify_yaml_custom
//
// This implementation can be used at build time to replace the default implementation
// to avoid linking with [gopkg.in/yaml.v3].
// to avoid linking with [go.yaml.in/yaml/v3].
//
// In your test package:
//
+3 -3
View File
@@ -6,7 +6,7 @@
// indirection with an alternative implementation of this package that uses
// another implementation of YAML deserialization. This allows to not either not
// use YAML deserialization at all, or to use another implementation than
// [gopkg.in/yaml.v3] (for example for license compatibility reasons, see [PR #1120]).
// [go.yaml.in/yaml/v3] (for example for license compatibility reasons, see [PR #1120]).
//
// Alternative implementations are selected using build tags:
//
@@ -28,9 +28,9 @@
// [PR #1120]: https://github.com/stretchr/testify/pull/1120
package yaml
import goyaml "gopkg.in/yaml.v3"
import goyaml "go.yaml.in/yaml/v3"
// Unmarshal is just a wrapper of [gopkg.in/yaml.v3.Unmarshal].
// Unmarshal is just a wrapper of [go.yaml.in/yaml/v3.Unmarshal].
func Unmarshal(in []byte, out interface{}) error {
return goyaml.Unmarshal(in, out)
}
+1 -1
View File
@@ -3,7 +3,7 @@
// Package yaml is an implementation of YAML functions that always fail.
//
// This implementation can be used at build time to replace the default implementation
// to avoid linking with [gopkg.in/yaml.v3]:
// to avoid linking with [go.yaml.in/yaml/v3]:
//
// go test -tags testify_yaml_fail
package yaml
@@ -24,4 +24,4 @@ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -8,11 +8,14 @@
//
// - unified_diff
//
// - context_diff
//
// Getting unified diffs was the main goal of the port. Keep in mind this code
// is mostly suitable to output text differences in a human friendly way, there
// are no guarantees generated diffs are consumable by patch(1).
//
// This package was adopted from [github.com/pmezard/go-difflib] which
// is no longer maintained.
//
// [github.com/pmezard/go-difflib]: https://github.com/pmezard/go-difflib
package difflib
import (
@@ -37,13 +40,6 @@ func max(a, b int) int {
return b
}
func calculateRatio(matches, length int) float64 {
if length > 0 {
return 2.0 * float64(matches) / float64(length)
}
return 1.0
}
type Match struct {
A int
B int
@@ -103,14 +99,6 @@ func NewMatcher(a, b []string) *SequenceMatcher {
return &m
}
func NewMatcherWithJunk(a, b []string, autoJunk bool,
isJunk func(string) bool) *SequenceMatcher {
m := SequenceMatcher{IsJunk: isJunk, autoJunk: autoJunk}
m.SetSeqs(a, b)
return &m
}
// Set two sequences to be compared.
func (m *SequenceMatcher) SetSeqs(a, b []string) {
m.SetSeq1(a)
@@ -199,12 +187,15 @@ func (m *SequenceMatcher) isBJunk(s string) bool {
// If IsJunk is not defined:
//
// Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where
// alo <= i <= i+k <= ahi
// blo <= j <= j+k <= bhi
//
// alo <= i <= i+k <= ahi
// blo <= j <= j+k <= bhi
//
// and for all (i',j',k') meeting those conditions,
// k >= k'
// i <= i'
// and if i == i', j <= j'
//
// k >= k'
// i <= i'
// and if i == i', j <= j'
//
// In other words, of all maximal matching blocks, return one that
// starts earliest in a, and of all those maximal matching blocks that
@@ -451,66 +442,6 @@ func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode {
return groups
}
// Return a measure of the sequences' similarity (float in [0,1]).
//
// Where T is the total number of elements in both sequences, and
// M is the number of matches, this is 2.0*M / T.
// Note that this is 1 if the sequences are identical, and 0 if
// they have nothing in common.
//
// .Ratio() is expensive to compute if you haven't already computed
// .GetMatchingBlocks() or .GetOpCodes(), in which case you may
// want to try .QuickRatio() or .RealQuickRation() first to get an
// upper bound.
func (m *SequenceMatcher) Ratio() float64 {
matches := 0
for _, m := range m.GetMatchingBlocks() {
matches += m.Size
}
return calculateRatio(matches, len(m.a)+len(m.b))
}
// Return an upper bound on ratio() relatively quickly.
//
// This isn't defined beyond that it is an upper bound on .Ratio(), and
// is faster to compute.
func (m *SequenceMatcher) QuickRatio() float64 {
// viewing a and b as multisets, set matches to the cardinality
// of their intersection; this counts the number of matches
// without regard to order, so is clearly an upper bound
if m.fullBCount == nil {
m.fullBCount = map[string]int{}
for _, s := range m.b {
m.fullBCount[s] = m.fullBCount[s] + 1
}
}
// avail[x] is the number of times x appears in 'b' less the
// number of times we've seen it in 'a' so far ... kinda
avail := map[string]int{}
matches := 0
for _, s := range m.a {
n, ok := avail[s]
if !ok {
n = m.fullBCount[s]
}
avail[s] = n - 1
if n > 0 {
matches += 1
}
}
return calculateRatio(matches, len(m.a)+len(m.b))
}
// Return an upper bound on ratio() very quickly.
//
// This isn't defined beyond that it is an upper bound on .Ratio(), and
// is faster to compute than either .Ratio() or .QuickRatio().
func (m *SequenceMatcher) RealQuickRatio() float64 {
la, lb := len(m.a), len(m.b)
return calculateRatio(min(la, lb), la+lb)
}
// Convert range to the "ed" format
func formatRangeUnified(start, stop int) string {
// Per the diff spec at http://www.unix.org/single_unix_specification/
@@ -652,117 +583,6 @@ func formatRangeContext(start, stop int) string {
return fmt.Sprintf("%d,%d", beginning, beginning+length-1)
}
type ContextDiff UnifiedDiff
// Compare two sequences of lines; generate the delta as a context diff.
//
// Context diffs are a compact way of showing line changes and a few
// lines of context. The number of context lines is set by diff.Context
// which defaults to three.
//
// By default, the diff control lines (those with *** or ---) are
// created with a trailing newline.
//
// For inputs that do not have trailing newlines, set the diff.Eol
// argument to "" so that the output will be uniformly newline free.
//
// The context diff format normally has a header for filenames and
// modification times. Any or all of these may be specified using
// strings for diff.FromFile, diff.ToFile, diff.FromDate, diff.ToDate.
// The modification times are normally expressed in the ISO 8601 format.
// If not specified, the strings default to blanks.
func WriteContextDiff(writer io.Writer, diff ContextDiff) error {
buf := bufio.NewWriter(writer)
defer buf.Flush()
var diffErr error
wf := func(format string, args ...interface{}) {
_, err := buf.WriteString(fmt.Sprintf(format, args...))
if diffErr == nil && err != nil {
diffErr = err
}
}
ws := func(s string) {
_, err := buf.WriteString(s)
if diffErr == nil && err != nil {
diffErr = err
}
}
if len(diff.Eol) == 0 {
diff.Eol = "\n"
}
prefix := map[byte]string{
'i': "+ ",
'd': "- ",
'r': "! ",
'e': " ",
}
started := false
m := NewMatcher(diff.A, diff.B)
for _, g := range m.GetGroupedOpCodes(diff.Context) {
if !started {
started = true
fromDate := ""
if len(diff.FromDate) > 0 {
fromDate = "\t" + diff.FromDate
}
toDate := ""
if len(diff.ToDate) > 0 {
toDate = "\t" + diff.ToDate
}
if diff.FromFile != "" || diff.ToFile != "" {
wf("*** %s%s%s", diff.FromFile, fromDate, diff.Eol)
wf("--- %s%s%s", diff.ToFile, toDate, diff.Eol)
}
}
first, last := g[0], g[len(g)-1]
ws("***************" + diff.Eol)
range1 := formatRangeContext(first.I1, last.I2)
wf("*** %s ****%s", range1, diff.Eol)
for _, c := range g {
if c.Tag == 'r' || c.Tag == 'd' {
for _, cc := range g {
if cc.Tag == 'i' {
continue
}
for _, line := range diff.A[cc.I1:cc.I2] {
ws(prefix[cc.Tag] + line)
}
}
break
}
}
range2 := formatRangeContext(first.J1, last.J2)
wf("--- %s ----%s", range2, diff.Eol)
for _, c := range g {
if c.Tag == 'r' || c.Tag == 'i' {
for _, cc := range g {
if cc.Tag == 'd' {
continue
}
for _, line := range diff.B[cc.J1:cc.J2] {
ws(prefix[cc.Tag] + line)
}
}
break
}
}
}
return diffErr
}
// Like WriteContextDiff but returns the diff a string.
func GetContextDiffString(diff ContextDiff) (string, error) {
w := &bytes.Buffer{}
err := WriteContextDiff(w, diff)
return string(w.Bytes()), err
}
// Split a string on "\n" while preserving them. The output can be used
// as input for UnifiedDiff and ContextDiff structures.
func SplitLines(s string) []string {
+12
View File
@@ -0,0 +1,12 @@
go-spew
=======
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
Go-spew implements a deep pretty printer for Go data structures to aid in
debugging. A comprehensive suite of tests with 100% test coverage is provided
to ensure proper functionality.
## License
Go-spew is licensed under the [copyfree](http://copyfree.org) ISC License.
@@ -18,6 +18,7 @@
// tag is deprecated and thus should not be used.
// Go versions prior to 1.4 are disabled because they use a different layout
// for interfaces which make the implementation of unsafeReflectValue more complex.
//go:build !js && !appengine && !safe && !disableunsafe && go1.4
// +build !js,!appengine,!safe,!disableunsafe,go1.4
package spew
@@ -16,6 +16,7 @@
// when the code is running on Google App Engine, compiled by GopherJS, or
// "-tags safe" is added to the go build command line. The "disableunsafe"
// tag is deprecated and thus should not be used.
//go:build js || appengine || safe || disableunsafe || !go1.4
// +build js appengine safe disableunsafe !go1.4
package spew
@@ -254,15 +254,15 @@ pointer addresses used to indirect to the final value. It provides the
following features over the built-in printing facilities provided by the fmt
package:
* Pointers are dereferenced and followed
* Circular data structures are detected and handled properly
* Custom Stringer/error interfaces are optionally invoked, including
on unexported types
* Custom types which only implement the Stringer/error interfaces via
a pointer receiver are optionally invoked when passing non-pointer
variables
* Byte arrays and slices are dumped like the hexdump -C command which
includes offsets, byte values in hex, and ASCII output
- Pointers are dereferenced and followed
- Circular data structures are detected and handled properly
- Custom Stringer/error interfaces are optionally invoked, including
on unexported types
- Custom types which only implement the Stringer/error interfaces via
a pointer receiver are optionally invoked when passing non-pointer
variables
- Byte arrays and slices are dumped like the hexdump -C command which
includes offsets, byte values in hex, and ASCII output
The configuration options are controlled by modifying the public members
of c. See ConfigState for options documentation.
@@ -295,12 +295,12 @@ func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{})
// NewDefaultConfig returns a ConfigState with the following default settings.
//
// Indent: " "
// MaxDepth: 0
// DisableMethods: false
// DisablePointerMethods: false
// ContinueOnMethod: false
// SortKeys: false
// Indent: " "
// MaxDepth: 0
// DisableMethods: false
// DisablePointerMethods: false
// ContinueOnMethod: false
// SortKeys: false
func NewDefaultConfig() *ConfigState {
return &ConfigState{Indent: " "}
}
@@ -21,35 +21,36 @@ debugging.
A quick overview of the additional features spew provides over the built-in
printing facilities for Go data types are as follows:
* Pointers are dereferenced and followed
* Circular data structures are detected and handled properly
* Custom Stringer/error interfaces are optionally invoked, including
on unexported types
* Custom types which only implement the Stringer/error interfaces via
a pointer receiver are optionally invoked when passing non-pointer
variables
* Byte arrays and slices are dumped like the hexdump -C command which
includes offsets, byte values in hex, and ASCII output (only when using
Dump style)
- Pointers are dereferenced and followed
- Circular data structures are detected and handled properly
- Custom Stringer/error interfaces are optionally invoked, including
on unexported types
- Custom types which only implement the Stringer/error interfaces via
a pointer receiver are optionally invoked when passing non-pointer
variables
- Byte arrays and slices are dumped like the hexdump -C command which
includes offsets, byte values in hex, and ASCII output (only when using
Dump style)
There are two different approaches spew allows for dumping Go data structures:
* Dump style which prints with newlines, customizable indentation,
and additional debug information such as types and all pointer addresses
used to indirect to the final value
* A custom Formatter interface that integrates cleanly with the standard fmt
package and replaces %v, %+v, %#v, and %#+v to provide inline printing
similar to the default %v while providing the additional functionality
outlined above and passing unsupported format verbs such as %x and %q
along to fmt
- Dump style which prints with newlines, customizable indentation,
and additional debug information such as types and all pointer addresses
used to indirect to the final value
- A custom Formatter interface that integrates cleanly with the standard fmt
package and replaces %v, %+v, %#v, and %#+v to provide inline printing
similar to the default %v while providing the additional functionality
outlined above and passing unsupported format verbs such as %x and %q
along to fmt
Quick Start
# Quick Start
This section demonstrates how to quickly get started with spew. See the
sections below for further details on formatting and configuration options.
To dump a variable with full newlines, indentation, type, and pointer
information use Dump, Fdump, or Sdump:
spew.Dump(myVar1, myVar2, ...)
spew.Fdump(someWriter, myVar1, myVar2, ...)
str := spew.Sdump(myVar1, myVar2, ...)
@@ -58,12 +59,13 @@ Alternatively, if you would prefer to use format strings with a compacted inline
printing style, use the convenience wrappers Printf, Fprintf, etc with
%v (most compact), %+v (adds pointer addresses), %#v (adds types), or
%#+v (adds types and pointer addresses):
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
Configuration Options
# Configuration Options
Configuration of spew is handled by fields in the ConfigState type. For
convenience, all of the top-level functions use a global state available
@@ -74,51 +76,52 @@ equivalent to the top-level functions. This allows concurrent configuration
options. See the ConfigState documentation for more details.
The following configuration options are available:
* Indent
String to use for each indentation level for Dump functions.
It is a single space by default. A popular alternative is "\t".
* MaxDepth
Maximum number of levels to descend into nested data structures.
There is no limit by default.
- Indent
String to use for each indentation level for Dump functions.
It is a single space by default. A popular alternative is "\t".
* DisableMethods
Disables invocation of error and Stringer interface methods.
Method invocation is enabled by default.
- MaxDepth
Maximum number of levels to descend into nested data structures.
There is no limit by default.
* DisablePointerMethods
Disables invocation of error and Stringer interface methods on types
which only accept pointer receivers from non-pointer variables.
Pointer method invocation is enabled by default.
- DisableMethods
Disables invocation of error and Stringer interface methods.
Method invocation is enabled by default.
* DisablePointerAddresses
DisablePointerAddresses specifies whether to disable the printing of
pointer addresses. This is useful when diffing data structures in tests.
- DisablePointerMethods
Disables invocation of error and Stringer interface methods on types
which only accept pointer receivers from non-pointer variables.
Pointer method invocation is enabled by default.
* DisableCapacities
DisableCapacities specifies whether to disable the printing of
capacities for arrays, slices, maps and channels. This is useful when
diffing data structures in tests.
- DisablePointerAddresses
DisablePointerAddresses specifies whether to disable the printing of
pointer addresses. This is useful when diffing data structures in tests.
* ContinueOnMethod
Enables recursion into types after invoking error and Stringer interface
methods. Recursion after method invocation is disabled by default.
- DisableCapacities
DisableCapacities specifies whether to disable the printing of
capacities for arrays, slices, maps and channels. This is useful when
diffing data structures in tests.
* SortKeys
Specifies map keys should be sorted before being printed. Use
this to have a more deterministic, diffable output. Note that
only native types (bool, int, uint, floats, uintptr and string)
and types which implement error or Stringer interfaces are
supported with other types sorted according to the
reflect.Value.String() output which guarantees display
stability. Natural map order is used by default.
- ContinueOnMethod
Enables recursion into types after invoking error and Stringer interface
methods. Recursion after method invocation is disabled by default.
* SpewKeys
Specifies that, as a last resort attempt, map keys should be
spewed to strings and sorted by those strings. This is only
considered if SortKeys is true.
- SortKeys
Specifies map keys should be sorted before being printed. Use
this to have a more deterministic, diffable output. Note that
only native types (bool, int, uint, floats, uintptr and string)
and types which implement error or Stringer interfaces are
supported with other types sorted according to the
reflect.Value.String() output which guarantees display
stability. Natural map order is used by default.
Dump Usage
- SpewKeys
Specifies that, as a last resort attempt, map keys should be
spewed to strings and sorted by those strings. This is only
considered if SortKeys is true.
# Dump Usage
Simply call spew.Dump with a list of variables you want to dump:
@@ -133,7 +136,7 @@ A third option is to call spew.Sdump to get the formatted output as a string:
str := spew.Sdump(myVar1, myVar2, ...)
Sample Dump Output
# Sample Dump Output
See the Dump example for details on the setup of the types and variables being
shown here.
@@ -150,13 +153,14 @@ shown here.
Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C
command as shown.
([]uint8) (len=32 cap=32) {
00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
00000020 31 32 |12|
}
Custom Formatter
# Custom Formatter
Spew provides a custom formatter that implements the fmt.Formatter interface
so that it integrates cleanly with standard fmt package printing functions. The
@@ -170,7 +174,7 @@ standard fmt package for formatting. In addition, the custom formatter ignores
the width and precision arguments (however they will still work on the format
specifiers not handled by the custom formatter).
Custom Formatter Usage
# Custom Formatter Usage
The simplest way to make use of the spew custom formatter is to call one of the
convenience functions such as spew.Printf, spew.Println, or spew.Printf. The
@@ -184,15 +188,17 @@ functions have syntax you are most likely already familiar with:
See the Index for the full list convenience functions.
Sample Formatter Output
# Sample Formatter Output
Double pointer to a uint8:
%v: <**>5
%+v: <**>(0xf8400420d0->0xf8400420c8)5
%#v: (**uint8)5
%#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5
Pointer to circular struct with a uint8 field and a pointer to itself:
%v: <*>{1 <*><shown>}
%+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)<shown>}
%#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)<shown>}
@@ -201,7 +207,7 @@ Pointer to circular struct with a uint8 field and a pointer to itself:
See the Printf example for details on the setup of variables being shown
here.
Errors
# Errors
Since it is possible for custom Stringer/error interfaces to panic, spew
detects them and handles them internally by printing the panic information
@@ -488,15 +488,15 @@ pointer addresses used to indirect to the final value. It provides the
following features over the built-in printing facilities provided by the fmt
package:
* Pointers are dereferenced and followed
* Circular data structures are detected and handled properly
* Custom Stringer/error interfaces are optionally invoked, including
on unexported types
* Custom types which only implement the Stringer/error interfaces via
a pointer receiver are optionally invoked when passing non-pointer
variables
* Byte arrays and slices are dumped like the hexdump -C command which
includes offsets, byte values in hex, and ASCII output
- Pointers are dereferenced and followed
- Circular data structures are detected and handled properly
- Custom Stringer/error interfaces are optionally invoked, including
on unexported types
- Custom types which only implement the Stringer/error interfaces via
a pointer receiver are optionally invoked when passing non-pointer
variables
- Byte arrays and slices are dumped like the hexdump -C command which
includes offsets, byte values in hex, and ASCII output
The configuration options are controlled by an exported package global,
spew.Config. See ConfigState for options documentation.
+1 -1
View File
@@ -1,4 +1,4 @@
# This is a renovate-friendly source of Docker images.
FROM python:3.13.6-slim-bullseye@sha256:e98b521460ee75bca92175c16247bdf7275637a8faaeb2bcfa19d879ae5c4b9a AS python
FROM otel/weaver:v0.25.0@sha256:bef6000b4a4be46f81242f9ee785e0ebf0604606c15f92cb54a59893a741ec0c AS weaver
FROM otel/weaver:v0.21.2@sha256:2401de985c38bdb98b43918e2f43aa36b2afed4aa5669ac1c1de0a17301cd36d AS weaver
FROM avtodev/markdown-lint:v1@sha256:6aeedc2f49138ce7a1cd0adffc1b1c0321b841dc2102408967d9301c031949ee AS markdown
+1 -1
View File
@@ -1 +1 @@
codespell==2.4.3
codespell==2.4.2
+82 -96
View File
@@ -226,9 +226,9 @@ func yaml_parser_state_machine(parser *yaml_parser_t, event *yaml_event_t) bool
}
// Parse the production:
// stream ::= STREAM-START implicit_document? explicit_document* STREAM-END
//
// ************
// stream ::= STREAM-START implicit_document? explicit_document* STREAM-END
// ************
func yaml_parser_parse_stream_start(parser *yaml_parser_t, event *yaml_event_t) bool {
token := peek_token(parser)
if token == nil {
@@ -249,13 +249,11 @@ func yaml_parser_parse_stream_start(parser *yaml_parser_t, event *yaml_event_t)
}
// Parse the productions:
// implicit_document ::= block_node DOCUMENT-END*
//
// *
//
// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
//
// *************************
// implicit_document ::= block_node DOCUMENT-END*
// *
// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
// *************************
func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t, implicit bool) bool {
token := peek_token(parser)
@@ -359,9 +357,9 @@ func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t
}
// Parse the productions:
// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
//
// ***********
// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
// ***********
func yaml_parser_parse_document_content(parser *yaml_parser_t, event *yaml_event_t) bool {
token := peek_token(parser)
if token == nil {
@@ -382,11 +380,10 @@ func yaml_parser_parse_document_content(parser *yaml_parser_t, event *yaml_event
}
// Parse the productions:
// implicit_document ::= block_node DOCUMENT-END*
//
// *************
//
// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
// implicit_document ::= block_node DOCUMENT-END*
// *************
// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
func yaml_parser_parse_document_end(parser *yaml_parser_t, event *yaml_event_t) bool {
token := peek_token(parser)
if token == nil {
@@ -432,42 +429,32 @@ func yaml_parser_set_event_comments(parser *yaml_parser_t, event *yaml_event_t)
}
// Parse the productions:
// block_node_or_indentless_sequence ::=
//
// ALIAS
// *****
// | properties (block_content | indentless_block_sequence)?
// ********** *
// | block_content | indentless_block_sequence
// *
//
// block_node ::= ALIAS
//
// *****
// | properties block_content?
// ********** *
// | block_content
// *
//
// flow_node ::= ALIAS
//
// *****
// | properties flow_content?
// ********** *
// | flow_content
// *
//
// properties ::= TAG ANCHOR? | ANCHOR TAG?
//
// *************************
//
// block_content ::= block_collection | flow_collection | SCALAR
//
// ******
//
// flow_content ::= flow_collection | SCALAR
//
// ******
// block_node_or_indentless_sequence ::=
// ALIAS
// *****
// | properties (block_content | indentless_block_sequence)?
// ********** *
// | block_content | indentless_block_sequence
// *
// block_node ::= ALIAS
// *****
// | properties block_content?
// ********** *
// | block_content
// *
// flow_node ::= ALIAS
// *****
// | properties flow_content?
// ********** *
// | flow_content
// *
// properties ::= TAG ANCHOR? | ANCHOR TAG?
// *************************
// block_content ::= block_collection | flow_collection | SCALAR
// ******
// flow_content ::= flow_collection | SCALAR
// ******
func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, indentless_sequence bool) bool {
//defer trace("yaml_parser_parse_node", "block:", block, "indentless_sequence:", indentless_sequence)()
@@ -697,9 +684,9 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i
}
// Parse the productions:
// block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END
//
// ******************** *********** * *********
// block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END
// ******************** *********** * *********
func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool {
if first {
token := peek_token(parser)
@@ -755,9 +742,9 @@ func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_e
}
// Parse the productions:
// indentless_sequence ::= (BLOCK-ENTRY block_node?)+
//
// *********** *
// indentless_sequence ::= (BLOCK-ENTRY block_node?)+
// *********** *
func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t, event *yaml_event_t) bool {
token := peek_token(parser)
if token == nil {
@@ -821,15 +808,15 @@ func yaml_parser_split_stem_comment(parser *yaml_parser_t, stem_len int) {
}
// Parse the productions:
// block_mapping ::= BLOCK-MAPPING_START
//
// *******************
// ((KEY block_node_or_indentless_sequence?)?
// *** *
// (VALUE block_node_or_indentless_sequence?)?)*
// block_mapping ::= BLOCK-MAPPING_START
// *******************
// ((KEY block_node_or_indentless_sequence?)?
// *** *
// (VALUE block_node_or_indentless_sequence?)?)*
//
// BLOCK-END
// *********
// BLOCK-END
// *********
func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool {
if first {
token := peek_token(parser)
@@ -896,13 +883,14 @@ func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_even
}
// Parse the productions:
// block_mapping ::= BLOCK-MAPPING_START
//
// ((KEY block_node_or_indentless_sequence?)?
// block_mapping ::= BLOCK-MAPPING_START
//
// (VALUE block_node_or_indentless_sequence?)?)*
// ***** *
// BLOCK-END
// ((KEY block_node_or_indentless_sequence?)?
//
// (VALUE block_node_or_indentless_sequence?)?)*
// ***** *
// BLOCK-END
func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool {
token := peek_token(parser)
if token == nil {
@@ -929,19 +917,17 @@ func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_ev
}
// Parse the productions:
// flow_sequence ::= FLOW-SEQUENCE-START
//
// *******************
// (flow_sequence_entry FLOW-ENTRY)*
// * **********
// flow_sequence_entry?
// *
// FLOW-SEQUENCE-END
// *****************
//
// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
//
// *
// flow_sequence ::= FLOW-SEQUENCE-START
// *******************
// (flow_sequence_entry FLOW-ENTRY)*
// * **********
// flow_sequence_entry?
// *
// FLOW-SEQUENCE-END
// *****************
// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
// *
func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool {
if first {
token := peek_token(parser)
@@ -1005,9 +991,9 @@ func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_ev
}
// Parse the productions:
// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
//
// *** *
// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
// *** *
func yaml_parser_parse_flow_sequence_entry_mapping_key(parser *yaml_parser_t, event *yaml_event_t) bool {
token := peek_token(parser)
if token == nil {
@@ -1026,9 +1012,9 @@ func yaml_parser_parse_flow_sequence_entry_mapping_key(parser *yaml_parser_t, ev
}
// Parse the productions:
// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
//
// ***** *
// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
// ***** *
func yaml_parser_parse_flow_sequence_entry_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool {
token := peek_token(parser)
if token == nil {
@@ -1050,9 +1036,9 @@ func yaml_parser_parse_flow_sequence_entry_mapping_value(parser *yaml_parser_t,
}
// Parse the productions:
// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
//
// *
// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
// *
func yaml_parser_parse_flow_sequence_entry_mapping_end(parser *yaml_parser_t, event *yaml_event_t) bool {
token := peek_token(parser)
if token == nil {
@@ -1068,18 +1054,17 @@ func yaml_parser_parse_flow_sequence_entry_mapping_end(parser *yaml_parser_t, ev
}
// Parse the productions:
// flow_mapping ::= FLOW-MAPPING-START
//
// ******************
// (flow_mapping_entry FLOW-ENTRY)*
// * **********
// flow_mapping_entry?
// ******************
// FLOW-MAPPING-END
// ****************
//
// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
// - *** *
// flow_mapping ::= FLOW-MAPPING-START
// ******************
// (flow_mapping_entry FLOW-ENTRY)*
// * **********
// flow_mapping_entry?
// ******************
// FLOW-MAPPING-END
// ****************
// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
// * *** *
func yaml_parser_parse_flow_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool {
if first {
token := peek_token(parser)
@@ -1144,8 +1129,9 @@ func yaml_parser_parse_flow_mapping_key(parser *yaml_parser_t, event *yaml_event
}
// Parse the productions:
// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
// - ***** *
//
// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
// * ***** *
func yaml_parser_parse_flow_mapping_value(parser *yaml_parser_t, event *yaml_event_t, empty bool) bool {
token := peek_token(parser)
if token == nil {
+20 -24
View File
@@ -433,21 +433,19 @@ type yaml_document_t struct {
// The prototype of a read handler.
//
// The read handler is called when the parser needs to read more bytes from the
// source. The handler should write not more than size bytes to the buffer.
// The number of written bytes should be set to the size_read variable.
// The read handler is called when the parser needs to read more bytes from the
// source. The handler should write not more than size bytes to the buffer.
// The number of written bytes should be set to the size_read variable.
//
// [in,out] data A pointer to an application data specified by
// [in,out] data A pointer to an application data specified by
// yaml_parser_set_input().
// [out] buffer The buffer to write the data from the source.
// [in] size The size of the buffer.
// [out] size_read The actual number of bytes read from the source.
//
// yaml_parser_set_input().
//
// [out] buffer The buffer to write the data from the source.
// [in] size The size of the buffer.
// [out] size_read The actual number of bytes read from the source.
//
// On success, the handler should return 1. If the handler failed,
// the returned value should be 0. On EOF, the handler should set the
// size_read to 0 and return 1.
// On success, the handler should return 1. If the handler failed,
// the returned value should be 0. On EOF, the handler should set the
// size_read to 0 and return 1.
type yaml_read_handler_t func(parser *yaml_parser_t, buffer []byte) (n int, err error)
// This structure holds information about a potential simple key.
@@ -655,19 +653,17 @@ type yaml_comment_t struct {
// The prototype of a write handler.
//
// The write handler is called when the emitter needs to flush the accumulated
// characters to the output. The handler should write @a size bytes of the
// @a buffer to the output.
// The write handler is called when the emitter needs to flush the accumulated
// characters to the output. The handler should write @a size bytes of the
// @a buffer to the output.
//
// @param[in,out] data A pointer to an application data specified by
// @param[in,out] data A pointer to an application data specified by
// yaml_emitter_set_output().
// @param[in] buffer The buffer with bytes to be written.
// @param[in] size The size of the buffer.
//
// yaml_emitter_set_output().
//
// @param[in] buffer The buffer with bytes to be written.
// @param[in] size The size of the buffer.
//
// @returns On success, the handler should return @c 1. If the handler failed,
// the returned value should be @c 0.
// @returns On success, the handler should return @c 1. If the handler failed,
// the returned value should be @c 0.
type yaml_write_handler_t func(emitter *yaml_emitter_t, buffer []byte) error
type yaml_emitter_state_t int
+18 -17
View File
@@ -17,8 +17,8 @@
// It uses data-independent memory access, which is preferred for password
// hashing and password-based key derivation. Argon2i requires more passes over
// memory than Argon2id to protect from trade-off attacks. The recommended
// parameters (taken from [RFC 9106 Section 7.3]) for non-interactive operations are time=3 and to
// use the maximum available memory.
// parameters (taken from [RFC 9106 Section 7.3]) for non-interactive
// operations are time=3 and to use the maximum available memory.
//
// # Argon2id
//
@@ -26,11 +26,14 @@
// Argon2i and Argon2d. It uses data-independent memory access for the first
// half of the first iteration over the memory and data-dependent memory access
// for the rest. Argon2id is side-channel resistant and provides better brute-
// force cost savings due to time-memory tradeoffs than Argon2i. The recommended
// parameters for non-interactive operations (taken from [RFC 9106 Section 7.3]) are time=1 and to
// use the maximum available memory.
// force cost savings due to time-memory tradeoffs than Argon2i. [RFC 9106
// Section 4] recommends time=1, memory=2*1024*1024 KiB (2 GiB), and threads=4
// as the first recommended option. If much less memory is available, it
// recommends time=3, memory=64*1024 KiB (64 MiB), and threads=4 as the second
// recommended option.
//
// [argon2-specs.pdf]: https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf
// [RFC 9106 Section 4]: https://www.rfc-editor.org/rfc/rfc9106.html#section-4
// [RFC 9106 Section 7.3]: https://www.rfc-editor.org/rfc/rfc9106.html#section-7.3
package argon2
@@ -59,9 +62,9 @@ const (
//
// key := argon2.Key([]byte("some password"), salt, 3, 32*1024, 4, 32)
//
// [RFC 9106 Section 7.3] recommends time=3, and memory=32*1024 as a sensible number.
// If using that amount of memory (32 MB) is not possible in some contexts then
// the time parameter can be increased to compensate.
// The example above uses time=3 and memory=32*1024. Argon2i generally
// requires more passes over memory than Argon2id. If in doubt, prefer IDKey
// and its Argon2id parameter recommendations.
//
// The time parameter specifies the number of passes over the memory and the
// memory parameter specifies the size of the memory in KiB. For example
@@ -69,8 +72,6 @@ const (
// adjusted to the number of available CPUs. The cost parameters should be
// increased as memory latency and CPU parallelism increases. Remember to get a
// good random salt.
//
// [RFC 9106 Section 7.3]: https://www.rfc-editor.org/rfc/rfc9106.html#section-7.3
func Key(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
return deriveKey(argon2i, password, salt, nil, nil, time, memory, threads, keyLen)
}
@@ -83,20 +84,20 @@ func Key(password, salt []byte, time, memory uint32, threads uint8, keyLen uint3
// For example, you can get a derived key for e.g. AES-256 (which needs a
// 32-byte key) by doing:
//
// key := argon2.IDKey([]byte("some password"), salt, 1, 64*1024, 4, 32)
// key := argon2.IDKey([]byte("some password"), salt, 1, 2*1024*1024, 4, 32)
//
// [RFC 9106 Section 7.3] recommends time=1, and memory=64*1024 as a sensible number.
// If using that amount of memory (64 MB) is not possible in some contexts then
// the time parameter can be increased to compensate.
// The example above uses the first [RFC 9106 Section 4] recommended option.
// If much less memory is available, the second recommended option is time=3,
// memory=64*1024 KiB (64 MiB), and threads=4.
//
// The time parameter specifies the number of passes over the memory and the
// memory parameter specifies the size of the memory in KiB. For example
// memory=64*1024 sets the memory cost to ~64 MB. The number of threads can be
// adjusted to the numbers of available CPUs. The cost parameters should be
// memory=2*1024*1024 sets the memory cost to ~2 GiB. The number of threads can
// be adjusted to the numbers of available CPUs. The cost parameters should be
// increased as memory latency and CPU parallelism increases. Remember to get a
// good random salt.
//
// [RFC 9106 Section 7.3]: https://www.rfc-editor.org/rfc/rfc9106.html#section-7.3
// [RFC 9106 Section 4]: https://www.rfc-editor.org/rfc/rfc9106.html#section-4
func IDKey(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
return deriveKey(argon2id, password, salt, nil, nil, time, memory, threads, keyLen)
}
-11
View File
@@ -1,11 +0,0 @@
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.25
package blake2b
import "hash"
var _ hash.XOF = (*xof)(nil)

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