Compare commits

..

1 Commits

Author SHA1 Message Date
Saw-jan
dfe2dcb0a0 test: setup for running wopi API tests locally
Signed-off-by: Saw-jan <saw.jan.grg3e@gmail.com>
2026-01-14 17:59:05 +05:45
65 changed files with 2503 additions and 3803 deletions

View File

@@ -576,7 +576,7 @@ def main(ctx):
),
)
pipelines = test_pipelines + build_release_pipelines + genDocsPr(ctx) + notifyMatrix(ctx)
pipelines = test_pipelines + build_release_pipelines + notifyMatrix(ctx)
pipelineSanityChecks(pipelines)
return savePipelineNumber(ctx) + pipelines
@@ -2213,60 +2213,6 @@ def makeGoGenerate(module):
},
]
def genDocsPr(ctx):
return [{
"name": "gen-docs-pr",
"skip_clone": True,
"workspace": {
"base": "/woodpecker",
"path": "docs_gen_pr",
},
"steps": [
{
"name": "make-docs-pr",
"image": "quay.io/opencloudeu/golang-ci",
"pull": True,
"environment": {
"GH_TOKEN": {
"from_secret": "github_token",
},
"CI_SSH_KEY": {
"from_secret": "markdown-docs-generator-push-key",
},
"CI_SSH_KEY_DOCS": {
"from_secret": "gh-docs-push-key",
},
"GIT_SSH_COMMAND": "ssh -o StrictHostKeyChecking=no -i /root/id_rsa",
"OC_GIT_BRANCH": "${CI_COMMIT_BRANCH}",
"MY_TARGET_BRANCH": "${CI_COMMIT_BRANCH}",
},
"commands": [
'export DOC_GIT_TARGET_FOLDER="$$(if [ \"$$MY_TARGET_BRANCH\" = \"main\" ]; then echo \"tmpdocs/docs/dev/_static/env-vars/\"; else echo \"tmpdocs/versioned_docs/version-$${MY_TARGET_BRANCH}/dev/_static/env-vars/\"; fi)"',
'echo "$${CI_SSH_KEY}" > /root/id_rsa && chmod 600 /root/id_rsa',
'git config --global user.email "devops@opencloud.eu"',
'git config --global user.name "openclouders"',
"git clone git@github.com:opencloud-eu/markdown-docs-generator.git /woodpecker/docs_gen_pr",
'echo "$${CI_SSH_KEY_DOCS}" > /root/id_rsa && chmod 600 /root/id_rsa',
"make git-clone",
"make all",
"make create-docs-pullrequest",
]
},
],
"when": [
{
"event": "push",
"path": "services/*/pkg/config/**/*.go",
"branch": "[main, stable-*]",
},
{
"event": "cron",
"branch": "[main]",
"cron": "nightly (@daily)",
},
],
}]
def notifyMatrix(ctx):
result = [{
"name": "chat-notifications",
@@ -3287,7 +3233,7 @@ def wopiCollaborationService(name):
environment["COLLABORATION_APP_ADDR"] = "https://onlyoffice"
environment["COLLABORATION_APP_ICON"] = "https://onlyoffice/web-apps/apps/documenteditor/main/resources/img/favicon.ico"
elif name == "fakeoffice":
environment["COLLABORATION_SERVICE_NAME"] = "collboration-fakeoficce"
environment["COLLABORATION_SERVICE_NAME"] = "collaboration-fakeoffice"
environment["COLLABORATION_APP_NAME"] = "FakeOffice"
environment["COLLABORATION_APP_PRODUCT"] = "Microsoft"
environment["COLLABORATION_APP_ADDR"] = "http://fakeoffice:8080"

View File

@@ -1,60 +1,5 @@
# Changelog
## [5.0.0](https://github.com/opencloud-eu/opencloud/releases/tag/v5.0.0) - 2026-01-14
### ❤️ Thanks to all contributors! ❤️
@ScharfViktor, @butonic, @dragonchaser, @flimmy, @fschade, @micbar, @rhafer, @saw-jan
### 💥 Breaking changes
- merge ocdav into frontend [[#1958](https://github.com/opencloud-eu/opencloud/pull/1958)]
### 📚 Documentation
- fix: markdown links formatting [[#2143](https://github.com/opencloud-eu/opencloud/pull/2143)]
### ✅ Tests
- ci: fix unwanted workflow skip in the cron pipelines [[#2117](https://github.com/opencloud-eu/opencloud/pull/2117)]
- [POC] ci: skip previously passed workflows on pipeline restart [[#2099](https://github.com/opencloud-eu/opencloud/pull/2099)]
- [tests-only] test: wait post-processing to finish for MKCOL requests [[#2092](https://github.com/opencloud-eu/opencloud/pull/2092)]
- [tests-only] test: fix API tests [[#2087](https://github.com/opencloud-eu/opencloud/pull/2087)]
- [full-ci] use graph api in the enforcePasswordPublicLink.feature [[#2050](https://github.com/opencloud-eu/opencloud/pull/2050)]
- [full-ci][tests-only] test: check last email content with retries as emails can be delayed [[#2038](https://github.com/opencloud-eu/opencloud/pull/2038)]
- skip collaborativePosix tests in CI [[#2039](https://github.com/opencloud-eu/opencloud/pull/2039)]
### 🐛 Bug Fixes
- fix: Show username in unprivileged search results [[#2104](https://github.com/opencloud-eu/opencloud/pull/2104)]
- fix(thumbnailer): missing font panic [[#2097](https://github.com/opencloud-eu/opencloud/pull/2097)]
- Remove sub-service binary entrypoints and fix antivirus only server cmd [[#2043](https://github.com/opencloud-eu/opencloud/pull/2043)]
- fix(thumbnailer): respect image boundaries and text wrappings [[#2062](https://github.com/opencloud-eu/opencloud/pull/2062)]
- fix: cobra viper flags and env [[#2047](https://github.com/opencloud-eu/opencloud/pull/2047)]
- fix service name in suture logs [[#2052](https://github.com/opencloud-eu/opencloud/pull/2052)]
### 📈 Enhancement
- benchmark client enhancements [[#1856](https://github.com/opencloud-eu/opencloud/pull/1856)]
- allow http2 connections to proxy [[#2040](https://github.com/opencloud-eu/opencloud/pull/2040)]
- migrate from urfave/cli to spf13/cobra [[#1954](https://github.com/opencloud-eu/opencloud/pull/1954)]
### 📦️ Dependencies
- build(deps): bump github.com/olekukonko/tablewriter from 1.1.1 to 1.1.2 [[#2144](https://github.com/opencloud-eu/opencloud/pull/2144)]
- build(deps): bump github.com/spf13/cobra from 1.10.1 to 1.10.2 [[#2141](https://github.com/opencloud-eu/opencloud/pull/2141)]
- build(deps): bump golang.org/x/net from 0.48.0 to 0.49.0 [[#2140](https://github.com/opencloud-eu/opencloud/pull/2140)]
- build(deps): bump github.com/onsi/gomega from 1.38.2 to 1.39.0 [[#2133](https://github.com/opencloud-eu/opencloud/pull/2133)]
- build(deps): bump golang.org/x/crypto from 0.46.0 to 0.47.0 [[#2132](https://github.com/opencloud-eu/opencloud/pull/2132)]
- build(deps): bump go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp from 0.63.0 to 0.64.0 [[#2109](https://github.com/opencloud-eu/opencloud/pull/2109)]
- build(deps): bump github.com/kovidgoyal/imaging from 1.8.17 to 1.8.18 [[#2107](https://github.com/opencloud-eu/opencloud/pull/2107)]
- build(deps): bump google.golang.org/grpc from 1.77.0 to 1.78.0 [[#2106](https://github.com/opencloud-eu/opencloud/pull/2106)]
- build(deps): bump go.opentelemetry.io/otel/sdk from 1.38.0 to 1.39.0 [[#2069](https://github.com/opencloud-eu/opencloud/pull/2069)]
- build(deps): bump github.com/opensearch-project/opensearch-go/v4 from 4.5.0 to 4.6.0 [[#2068](https://github.com/opencloud-eu/opencloud/pull/2068)]
- build(deps): bump github.com/testcontainers/testcontainers-go/modules/opensearch from 0.39.0 to 0.40.0 [[#1967](https://github.com/opencloud-eu/opencloud/pull/1967)]
- build(deps): bump golang.org/x/net from 0.47.0 to 0.48.0 [[#2061](https://github.com/opencloud-eu/opencloud/pull/2061)]
- build(deps): bump github.com/open-policy-agent/opa from 1.10.1 to 1.11.0 [[#1930](https://github.com/opencloud-eu/opencloud/pull/1930)]
## [4.1.0](https://github.com/opencloud-eu/opencloud/releases/tag/v4.1.0) - 2025-12-15
### ❤️ Thanks to all contributors! ❤️

26
go.mod
View File

@@ -57,10 +57,10 @@ require (
github.com/nats-io/nats-server/v2 v2.12.3
github.com/nats-io/nats.go v1.47.0
github.com/oklog/run v1.2.0
github.com/olekukonko/tablewriter v1.1.2
github.com/olekukonko/tablewriter v1.1.1
github.com/onsi/ginkgo v1.16.5
github.com/onsi/ginkgo/v2 v2.27.2
github.com/onsi/gomega v1.39.0
github.com/onsi/gomega v1.38.2
github.com/open-policy-agent/opa v1.11.1
github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20250724122329-41ba6b191e76
@@ -77,7 +77,7 @@ require (
github.com/rs/zerolog v1.34.0
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af
github.com/spf13/afero v1.15.0
github.com/spf13/cobra v1.10.2
github.com/spf13/cobra v1.10.1
github.com/spf13/pflag v1.0.10
github.com/spf13/viper v1.21.0
github.com/stretchr/testify v1.11.1
@@ -102,14 +102,14 @@ require (
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0
go.opentelemetry.io/otel/sdk v1.39.0
go.opentelemetry.io/otel/trace v1.39.0
golang.org/x/crypto v0.47.0
golang.org/x/crypto v0.46.0
golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac
golang.org/x/image v0.34.0
golang.org/x/net v0.49.0
golang.org/x/net v0.48.0
golang.org/x/oauth2 v0.34.0
golang.org/x/sync v0.19.0
golang.org/x/term v0.39.0
golang.org/x/text v0.33.0
golang.org/x/term v0.38.0
golang.org/x/text v0.32.0
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217
google.golang.org/grpc v1.78.0
google.golang.org/protobuf v1.36.11
@@ -165,9 +165,9 @@ require (
github.com/ceph/go-ceph v0.37.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cevaris/ordered_map v0.0.0-20190319150403-3adeae072e73 // indirect
github.com/clipperhouse/displaywidth v0.6.0 // indirect
github.com/clipperhouse/displaywidth v0.3.1 // indirect
github.com/clipperhouse/stringish v0.1.1 // indirect
github.com/clipperhouse/uax29/v2 v2.3.0 // indirect
github.com/clipperhouse/uax29/v2 v2.2.0 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
@@ -312,7 +312,7 @@ require (
github.com/nxadm/tail v1.4.8 // indirect
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect
github.com/olekukonko/errors v1.1.0 // indirect
github.com/olekukonko/ll v0.1.3 // indirect
github.com/olekukonko/ll v0.1.2 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
@@ -390,10 +390,10 @@ require (
go.uber.org/zap v1.27.0 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/mod v0.31.0 // indirect
golang.org/x/sys v0.40.0 // indirect
golang.org/x/mod v0.30.0 // indirect
golang.org/x/sys v0.39.0 // indirect
golang.org/x/time v0.14.0 // indirect
golang.org/x/tools v0.40.0 // indirect
golang.org/x/tools v0.39.0 // indirect
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect

52
go.sum
View File

@@ -223,12 +223,12 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/clipperhouse/displaywidth v0.6.0 h1:k32vueaksef9WIKCNcoqRNyKbyvkvkysNYnAWz2fN4s=
github.com/clipperhouse/displaywidth v0.6.0/go.mod h1:R+kHuzaYWFkTm7xoMmK1lFydbci4X2CicfbGstSGg0o=
github.com/clipperhouse/displaywidth v0.3.1 h1:k07iN9gD32177o1y4O1jQMzbLdCrsGJh+blirVYybsk=
github.com/clipperhouse/displaywidth v0.3.1/go.mod h1:tgLJKKyaDOCadywag3agw4snxS5kYEuYR6Y9+qWDDYM=
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4=
github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY=
github.com/clipperhouse/uax29/v2 v2.2.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304=
@@ -940,11 +940,11 @@ github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0=
github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM=
github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
github.com/olekukonko/ll v0.1.3 h1:sV2jrhQGq5B3W0nENUISCR6azIPf7UBUpVq0x/y70Fg=
github.com/olekukonko/ll v0.1.3/go.mod h1:b52bVQRRPObe+yyBl0TxNfhesL0nedD4Cht0/zx55Ew=
github.com/olekukonko/ll v0.1.2 h1:lkg/k/9mlsy0SxO5aC+WEpbdT5K83ddnNhAepz7TQc0=
github.com/olekukonko/ll v0.1.2/go.mod h1:b52bVQRRPObe+yyBl0TxNfhesL0nedD4Cht0/zx55Ew=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/olekukonko/tablewriter v1.1.2 h1:L2kI1Y5tZBct/O/TyZK1zIE9GlBj/TVs+AY5tZDCDSc=
github.com/olekukonko/tablewriter v1.1.2/go.mod h1:z7SYPugVqGVavWoA2sGsFIoOVNmEHxUAAMrhXONtfkg=
github.com/olekukonko/tablewriter v1.1.1 h1:b3reP6GCfrHwmKkYwNRFh2rxidGHcT6cgxj/sHiDDx0=
github.com/olekukonko/tablewriter v1.1.1/go.mod h1:De/bIcTF+gpBDB3Alv3fEsZA+9unTsSzAg/ZGADCtn4=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
@@ -955,8 +955,8 @@ github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zw
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.39.0 h1:y2ROC3hKFmQZJNFeGAMeHZKkjBL65mIZcvrLQBF9k6Q=
github.com/onsi/gomega v1.39.0/go.mod h1:ZCU1pkQcXDO5Sl9/VVEGlDyp+zm0m1cmeG5TOzLgdh4=
github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A=
github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k=
github.com/open-policy-agent/opa v1.11.1 h1:4bMlG6DjRZTRAswRyF+KUCgxHu1Gsk0h9EbZ4W9REvM=
github.com/open-policy-agent/opa v1.11.1/go.mod h1:QimuJO4T3KYxWzrmAymqlFvsIanCjKrGjmmC8GgAdgE=
github.com/opencloud-eu/go-micro-plugins/v4/store/nats-js-kv v0.0.0-20250512152754-23325793059a h1:Sakl76blJAaM6NxylVkgSzktjo2dS504iDotEFJsh3M=
@@ -1164,8 +1164,8 @@ github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
@@ -1375,8 +1375,8 @@ golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -1418,8 +1418,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1473,8 +1473,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
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=
@@ -1586,8 +1586,8 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
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=
@@ -1599,8 +1599,8 @@ golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
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=
@@ -1615,8 +1615,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -1679,8 +1679,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
golang.org/x/tools/godoc v0.1.0-deprecated h1:o+aZ1BOj6Hsx/GBdJO/s815sqftjSnrZZwyYTHODvtk=
golang.org/x/tools/godoc v0.1.0-deprecated/go.mod h1:qM63CriJ961IHWmnWa9CjZnBndniPt4a3CK0PVB9bIg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@@ -5,7 +5,6 @@ import (
"bytes"
"fmt"
"io"
"regexp"
"strings"
)
@@ -78,7 +77,7 @@ func (md MD) WriteToc(w io.Writer) (int64, error) {
// main title not in toc
continue
}
link := fmt.Sprintf("#%s", toAnchor(h.Header))
link := fmt.Sprintf("#%s", strings.ToLower(strings.Replace(h.Header, " ", "-", -1)))
s := fmt.Sprintf("%s* [%s](%s)\n", strings.Repeat(" ", h.Level-2), h.Header, link)
n, err := w.Write([]byte(s))
if err != nil {
@@ -138,12 +137,3 @@ func headingFromString(s string) Heading {
Header: strings.TrimPrefix(con, " "),
}
}
func toAnchor(header string) string {
// Remove everything except letters, numbers, and spaces
reg := regexp.MustCompile(`[^a-zA-Z0-9 ]+`)
anchor := reg.ReplaceAllString(header, "")
// Replace spaces with hyphens and convert to lowercase
anchor = strings.ReplaceAll(anchor, " ", "-")
return strings.ToLower(anchor)
}

View File

@@ -1,3 +1,4 @@
.ONESHELL:
SHELL := bash
# define standard colors
@@ -24,7 +25,7 @@ endif
## user input
BEHAT_FEATURE ?=
TEST_SOURCE ?= opencloud
ifdef OC_IMAGE_TAG
BUILD_DEV_IMAGE := 0
@@ -37,13 +38,9 @@ OC_IMAGE_TAG ?= dev
WITH_WRAPPER ?= true
OC_WRAPPER := ../../ocwrapper/bin/ocwrapper
ifdef START_TIKA
ifeq ($(START_TIKA),true)
COMPOSE_FILE := $(COMPOSE_FILE):src/tika.yml
SEARCH_EXTRACTOR_TYPE := tika
else
SEARCH_EXTRACTOR_TYPE := basic
endif
ifeq ($(START_TIKA),true)
COMPOSE_FILE := $(COMPOSE_FILE):src/tika.yml
SEARCH_EXTRACTOR_TYPE := tika
else
SEARCH_EXTRACTOR_TYPE := basic
endif
@@ -224,28 +221,25 @@ $(targets):
.PHONY: testSuite
testSuite: $(OC_WRAPPER) build-dev-image clean-docker-container
@if [ -n "${START_CEPH}" ]; then \
COMPOSE_PROJECT_NAME=$(COMPOSE_PROJECT_NAME) \
COMPOSE_FILE=src/ceph.yml \
docker compose run start_ceph; \
fi; \
if [ "${START_EMAIL}" == "true" ]; then \
COMPOSE_PROJECT_NAME=$(COMPOSE_PROJECT_NAME) \
COMPOSE_FILE=src/email.yml \
docker compose run start_email; \
fi; \
if [ "${START_ANTIVIRUS}" == "true" ]; then \
COMPOSE_PROJECT_NAME=$(COMPOSE_PROJECT_NAME) \
COMPOSE_FILE=src/antivirus.yml \
docker compose run start_antivirus; \
fi; \
if [ "${START_TIKA}" == "true" ]; then \
COMPOSE_PROJECT_NAME=$(COMPOSE_PROJECT_NAME) \
COMPOSE_FILE=src/tika.yml \
docker compose run tika-service; \
fi; \
COMPOSE_PROJECT_NAME=$(COMPOSE_PROJECT_NAME) \
COMPOSE_FILE=$(COMPOSE_FILE) \
@export COMPOSE_PROJECT_NAME=$(COMPOSE_PROJECT_NAME)
COMPOSE_FILE=$(COMPOSE_FILE)
if [ "$$STORAGE_DRIVER" = "decomposeds3" ]; then
docker compose -f src/ceph.yml run start_ceph
fi
if [ "$$START_EMAIL" = "true" ]; then
docker compose -f src/email.yml run start_email
fi
if [ "$$START_ANTIVIRUS" = "true" ]; then
docker compose -f src/antivirus.yml run start_antivirus
fi
if [ "$$START_TIKA" = "true" ]; then
docker compose -f src/tika.yml run tika-service
fi
if [ "$$ENABLE_WOPI" = "true" ]; then
COMPOSE_FILE=${COMPOSE_FILE}:src/wopi.yml
fi
COMPOSE_FILE=$$COMPOSE_FILE \
STORAGE_DRIVER=$(STORAGE_DRIVER) \
TEST_SOURCE=$(TEST_SOURCE) \
WITH_WRAPPER=$(WITH_WRAPPER) \
@@ -263,13 +257,11 @@ testSuite: $(OC_WRAPPER) build-dev-image clean-docker-container
.PHONY: show-test-logs
show-test-logs: ## show logs of test
@COMPOSE_PROJECT_NAME=$(COMPOSE_PROJECT_NAME) \
COMPOSE_FILE=$(COMPOSE_FILE) \
docker compose logs --no-log-prefix -f acceptance-tests | less
.PHONY: ps
ps: ## show docker status
@COMPOSE_PROJECT_NAME=$(COMPOSE_PROJECT_NAME) \
COMPOSE_FILE=$(COMPOSE_FILE) \
docker compose ps
$(OC_WRAPPER):
@@ -285,36 +277,25 @@ build-dev-image:
.PHONY: clean-dev-docker-image
clean-dev-docker-image: ## clean docker image built during acceptance tests
@docker image rm opencloud-eu/opencloud:dev || true
@docker image rm opencloudeu/opencloud:dev || true
.PHONY: clean-docker-container
clean-docker-container: ## clean docker containers created during acceptance tests
@COMPOSE_PROJECT_NAME=$(COMPOSE_PROJECT_NAME) \
COMPOSE_FILE=$(COMPOSE_FILE) \
BEHAT_SUITE="" \
DIVIDE_INTO_NUM_PARTS="" \
OC_IMAGE_TAG="" \
RUN_PART="" \
STORAGE_DRIVER="" \
TEST_SOURCE="" \
docker compose down --remove-orphans
.PHONY: clean-docker-volumes
clean-docker-volumes: ## clean docker volumes created during acceptance tests
@COMPOSE_PROJECT_NAME=$(COMPOSE_PROJECT_NAME) \
COMPOSE_FILE=$(COMPOSE_FILE) \
BEHAT_SUITE="" \
DIVIDE_INTO_NUM_PARTS="" \
OC_IMAGE_TAG="" \
RUN_PART="" \
STORAGE_DRIVER="" \
TEST_SOURCE="" \
docker compose down --remove-orphans -v
.PHONY: clean-files
clean-files:
@$(MAKE) --no-print-directory -C ../../../. clean-tests
.PHONY: clean
clean-compose: clean-docker-container clean-docker-volumes clean-files
.PHONY: clean
clean: clean-docker-container clean-docker-volumes clean-dev-docker-image clean-files ## clean all
@@ -328,6 +309,17 @@ start-server: $(OC_WRAPPER) ## build and start server
TEST_SOURCE=opencloud \
STORAGE_DRIVER=$(STORAGE_DRIVER) \
OC_ASYNC_UPLOADS=true \
SEARCH_EXTRACTOR_TYPE=tika \
SEARCH_EXTRACTOR_TYPE=$(SEARCH_EXTRACTOR_TYPE) \
OC_ADD_RUN_SERVICES=notifications \
docker compose up -d --build --force-recreate
.PHONY: run-test
run-test:
COMPOSE_PROJECT_NAME=$(COMPOSE_PROJECT_NAME) \
STORAGE_DRIVER=$(STORAGE_DRIVER) \
TEST_SOURCE=$(TEST_SOURCE) \
BEHAT_SUITE=$(BEHAT_SUITE) \
BEHAT_FEATURE=$(BEHAT_FEATURE) \
DIVIDE_INTO_NUM_PARTS=$(DIVIDE_INTO_NUM_PARTS) \
RUN_PART=$(RUN_PART) \
docker compose -f src/acceptance.yml up

View File

@@ -0,0 +1,12 @@
<wopi-discovery>
<net-zone name="external-http">
<app favIconUrl="https://fakeoffice.opencloud.test/favicon.ico" name="wopitest">
<action default="true" ext="wopitest" name="view" urlsrc="https://fakeoffice.opencloud.test/not/relevant?"/>
<action default="true" ext="wopitest" name="edit" urlsrc="https://fakeoffice.opencloud.test/not/relevant?"/>
<action default="true" ext="odt" name="view" urlsrc="https://fakeoffice.opencloud.test/not/relevant?"/>
<action default="true" ext="odt" name="edit" urlsrc="https://fakeoffice.opencloud.test/not/relevant?"/>
<action default="true" ext="txt" name="view" urlsrc="https://fakeoffice.opencloud.test/not/relevant?"/>
<action default="true" ext="txt" name="edit" urlsrc="https://fakeoffice.opencloud.test/not/relevant?"/>
</app>
</net-zone>
</wopi-discovery>

View File

@@ -0,0 +1,10 @@
#!/bin/bash
set -e
mkdir -p /var/www/onlyoffice/Data/certs
cd /var/www/onlyoffice/Data/certs
openssl req -x509 -newkey rsa:4096 -keyout onlyoffice.key -out onlyoffice.crt -sha256 -days 365 -batch -nodes
chmod 400 /var/www/onlyoffice/Data/certs/onlyoffice.key
/app/ds/run-document-server.sh

View File

@@ -1,7 +1,7 @@
services:
opencloud-server:
image: opencloudeu/opencloud:dev
entrypoint: [ "/bin/sh", "/usr/bin/serve-opencloud.sh" ]
entrypoint: ["/bin/sh", "/usr/bin/serve-opencloud.sh"]
user: root
environment:
WITH_WRAPPER: $WITH_WRAPPER
@@ -22,7 +22,6 @@ services:
OC_ASYNC_UPLOADS: $OC_ASYNC_UPLOADS
OC_ADD_RUN_SERVICES: $OC_ADD_RUN_SERVICES
PROXY_HTTP_ADDR: "0.0.0.0:9200"
OC_JWT_SECRET: "some-random-jwt-secret"
# decomposeds3 specific settings
STORAGE_USERS_DECOMPOSEDS3_ENDPOINT: http://ceph:8080
@@ -52,9 +51,9 @@ services:
# fonts map for txt thumbnails (including unicode support)
THUMBNAILS_TXT_FONTMAP_FILE: "/woodpecker/src/github.com/opencloud-eu/opencloud/tests/config/drone/fontsMap.json"
ports:
- '9200:9200'
- '5200:5200' ## ocwrapper
- '9174:9174' ## notifications debug
- "9200:9200"
- "5200:5200" ## ocwrapper
- "9174:9174" ## notifications debug
volumes:
- ../../../config:/woodpecker/src/github.com/opencloud-eu/opencloud/tests/config
- ../../../ocwrapper/bin/ocwrapper:/usr/bin/ocwrapper

View File

@@ -0,0 +1,105 @@
x-common_config: &common_config
image: opencloudeu/opencloud:dev
restart: unless-stopped
entrypoint: /bin/sh
command: ["-c", "opencloud collaboration server"]
user: root
x-common_env: &common_env
OC_CONFIG_DIR: /etc/opencloud
MICRO_REGISTRY: nats-js-kv
MICRO_REGISTRY_ADDRESS: opencloud-server:9233
COLLABORATION_LOG_LEVEL: info
COLLABORATION_GRPC_ADDR: 0.0.0.0:9301
COLLABORATION_HTTP_ADDR: 0.0.0.0:9300
COLLABORATION_DEBUG_ADDR: 0.0.0.0:9304
COLLABORATION_APP_PROOF_DISABLE: true
COLLABORATION_APP_INSECURE: true
COLLABORATION_CS3API_DATAGATEWAY_INSECURE: true
COLLABORATION_WOPI_SECRET: some-wopi-secret
x-config_volume: &config_volume
- config:/etc/opencloud
x-depends_on: &depends_on
- opencloud-server
services:
opencloud-server:
environment:
OC_CONFIG_DIR: /etc/opencloud
GATEWAY_GRPC_ADDR: 0.0.0.0:9142
NATS_NATS_HOST: 0.0.0.0
NATS_NATS_PORT: 9233
volumes: *config_volume
fakeoffice:
image: alpine:latest
entrypoint: /bin/sh
command:
[
"-c",
"while true; do echo -e \"HTTP/1.1 200 OK\n\n$(cat /fakeoffice-discovery.xml)\" | nc -l -k -p 8080; done",
]
healthcheck:
test: ["CMD", "curl", "-f", "http://fakeoffice:8080"]
volumes:
- ./fakeoffice-discovery.xml:/fakeoffice-discovery.xml
collabora:
image: collabora/code:24.04.5.1.1
environment:
DONT_GEN_SSL_CERT: set
extra_params: --o:ssl.enable=true --o:ssl.termination=true --o:welcome.enable=false --o:net.frame_ancestors=https://opencloud-server:9200
entrypoint: /bin/sh
command: ["-c", "coolconfig generate-proof-key; /start-collabora-online.sh"]
onlyoffice:
image: onlyoffice/documentserver:7.5.1
environment:
WOPI_ENABLED: true
USE_UNAUTHORIZED_STORAGE: true
entrypoint: bash /entrypoint.sh
volumes:
- ./onlyoffice-entrypoint.sh:/entrypoint.sh
collaboration-fakeoffice:
<<: *common_config
environment:
<<: *common_env
COLLABORATION_SERVICE_NAME: collaboration-fakeoffice
COLLABORATION_APP_NAME: FakeOffice
COLLABORATION_APP_PRODUCT: Microsoft
COLLABORATION_APP_ADDR: http://fakeoffice:8080
COLLABORATION_WOPI_SRC: http://collaboration-fakeoffice:9300
volumes: *config_volume
depends_on: *depends_on
collaboration-collabora:
<<: *common_config
environment:
<<: *common_env
COLLABORATION_SERVICE_NAME: collaboration-collabora
COLLABORATION_APP_NAME: Collabora
COLLABORATION_APP_PRODUCT: Collabora
COLLABORATION_APP_ADDR: https://collabora:9980
COLLABORATION_APP_ICON: https://collabora:9980/favicon.ico
COLLABORATION_WOPI_SRC: http://collaboration-collabora:9300
volumes: *config_volume
depends_on: *depends_on
collaboration-onlyoffice:
<<: *common_config
environment:
<<: *common_env
COLLABORATION_SERVICE_NAME: collaboration-onlyoffice
COLLABORATION_APP_NAME: OnlyOffice
COLLABORATION_APP_PRODUCT: OnlyOffice
COLLABORATION_APP_ADDR: https://onlyoffice
COLLABORATION_APP_ICON: https://onlyoffice/web-apps/apps/documenteditor/main/resources/img/favicon.ico
COLLABORATION_WOPI_SRC: http://collaboration-onlyoffice:9300
volumes: *config_volume
depends_on: *depends_on
volumes:
config:

View File

@@ -1,60 +0,0 @@
# Changelog
## [0.6.0]
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.5.0...v0.6.0)
### Added
- New `StringGraphemes` and `BytesGraphemes` methods, for iterating over the
widths of grapheme clusters.
### Changed
- Added ASCII fast paths
## [0.5.0]
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.4.1...v0.5.0)
### Added
- Unicode 16 support
- Improved emoji presentation handling per Unicode TR51
### Changed
- Corrected VS15 (U+FE0E) handling: now preserves base character width (no-op) per Unicode TR51
- Performance optimizations: reduced property lookups
### Fixed
- VS15 variation selector now correctly preserves base character width instead of forcing width 1
## [0.4.1]
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.4.0...v0.4.1)
### Changed
- Updated uax29 dependency
- Improved flag handling
## [0.4.0]
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.3.1...v0.4.0)
### Added
- Support for variation selectors (VS15, VS16) and regional indicator pairs (flags)
## [0.3.1]
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.3.0...v0.3.1)
### Added
- Fuzz testing support
### Changed
- Updated stringish dependency
## [0.3.0]
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.2.0...v0.3.0)
### Changed
- Dropped compatibility with go-runewidth
- Trie implementation cleanup

View File

@@ -5,7 +5,6 @@ A high-performance Go package for measuring the monospace display width of strin
[![Documentation](https://pkg.go.dev/badge/github.com/clipperhouse/displaywidth.svg)](https://pkg.go.dev/github.com/clipperhouse/displaywidth)
[![Test](https://github.com/clipperhouse/displaywidth/actions/workflows/gotest.yml/badge.svg)](https://github.com/clipperhouse/displaywidth/actions/workflows/gotest.yml)
[![Fuzz](https://github.com/clipperhouse/displaywidth/actions/workflows/gofuzz.yml/badge.svg)](https://github.com/clipperhouse/displaywidth/actions/workflows/gofuzz.yml)
## Install
```bash
go get github.com/clipperhouse/displaywidth
@@ -33,91 +32,84 @@ func main() {
}
```
For most purposes, you should use the `String` or `Bytes` methods.
### Options
You can specify East Asian Width settings. When false (default),
[East Asian Ambiguous characters](https://www.unicode.org/reports/tr11/#Ambiguous)
are treated as width 1. When true, East Asian Ambiguous characters are treated
as width 2.
You can specify East Asian Width and Strict Emoji Neutral settings. If
unspecified, the default is `EastAsianWidth: false, StrictEmojiNeutral: true`.
```go
myOptions := displaywidth.Options{
EastAsianWidth: true,
options := displaywidth.Options{
EastAsianWidth: true,
StrictEmojiNeutral: false,
}
width := myOptions.String("Hello, 世界!")
width := options.String("Hello, 世界!")
fmt.Println(width)
```
## Technical details
## Details
This package implements the Unicode East Asian Width standard
([UAX #11](https://www.unicode.org/reports/tr11/)), and handles
[version selectors](https://en.wikipedia.org/wiki/Variation_Selectors_(Unicode_block)),
and [regional indicator pairs](https://en.wikipedia.org/wiki/Regional_indicator_symbol)
(flags). We implement [Unicode TR51](https://unicode.org/reports/tr51/).
`clipperhouse/displaywidth`, `mattn/go-runewidth`, and `rivo/uniseg` will
give the same outputs for most real-world text. See extensive details in the
[compatibility analysis](comparison/COMPATIBILITY_ANALYSIS.md).
If you wish to investigate the core logic, see the `lookupProperties` and `width`
functions in [width.go](width.go#L135). The essential trie generation logic is in
`buildPropertyBitmap` in [unicode.go](internal/gen/unicode.go#L317).
I (@clipperhouse) am keeping an eye on [emerging standards and test suites](https://www.jeffquast.com/post/state-of-terminal-emulation-2025/).
This package implements the Unicode East Asian Width standard (UAX #11) and is
intended to be compatible with `go-runewidth`. It operates on bytes without
decoding runes for better performance.
## Prior Art
[mattn/go-runewidth](https://github.com/mattn/go-runewidth)
[rivo/uniseg](https://github.com/rivo/uniseg)
[x/text/width](https://pkg.go.dev/golang.org/x/text/width)
[x/text/internal/triegen](https://pkg.go.dev/golang.org/x/text/internal/triegen)
## Benchmarks
Part of my motivation is the insight that we can avoid decoding runes for better performance.
```bash
cd comparison
go test -bench=. -benchmem
```
```
goos: darwin
goarch: arm64
pkg: github.com/clipperhouse/displaywidth/comparison
pkg: github.com/clipperhouse/displaywidth
cpu: Apple M2
BenchmarkString_Mixed/clipperhouse/displaywidth-8 10469 ns/op 161.15 MB/s 0 B/op 0 allocs/op
BenchmarkString_Mixed/mattn/go-runewidth-8 14250 ns/op 118.39 MB/s 0 B/op 0 allocs/op
BenchmarkString_Mixed/rivo/uniseg-8 19258 ns/op 87.60 MB/s 0 B/op 0 allocs/op
BenchmarkString_EastAsian/clipperhouse/displaywidth-8 10518 ns/op 160.39 MB/s 0 B/op 0 allocs/op
BenchmarkString_EastAsian/mattn/go-runewidth-8 23827 ns/op 70.80 MB/s 0 B/op 0 allocs/op
BenchmarkString_EastAsian/rivo/uniseg-8 19537 ns/op 86.35 MB/s 0 B/op 0 allocs/op
BenchmarkString_ASCII/clipperhouse/displaywidth-8 1027 ns/op 124.61 MB/s 0 B/op 0 allocs/op
BenchmarkString_ASCII/mattn/go-runewidth-8 1166 ns/op 109.78 MB/s 0 B/op 0 allocs/op
BenchmarkString_ASCII/rivo/uniseg-8 1551 ns/op 82.52 MB/s 0 B/op 0 allocs/op
BenchmarkString_Emoji/clipperhouse/displaywidth-8 3164 ns/op 228.84 MB/s 0 B/op 0 allocs/op
BenchmarkString_Emoji/mattn/go-runewidth-8 4728 ns/op 153.13 MB/s 0 B/op 0 allocs/op
BenchmarkString_Emoji/rivo/uniseg-8 6489 ns/op 111.57 MB/s 0 B/op 0 allocs/op
BenchmarkRune_Mixed/clipperhouse/displaywidth-8 3429 ns/op 491.96 MB/s 0 B/op 0 allocs/op
BenchmarkRune_Mixed/mattn/go-runewidth-8 5308 ns/op 317.81 MB/s 0 B/op 0 allocs/op
BenchmarkRune_EastAsian/clipperhouse/displaywidth-8 3419 ns/op 493.49 MB/s 0 B/op 0 allocs/op
BenchmarkRune_EastAsian/mattn/go-runewidth-8 15321 ns/op 110.11 MB/s 0 B/op 0 allocs/op
BenchmarkRune_ASCII/clipperhouse/displaywidth-8 254.4 ns/op 503.19 MB/s 0 B/op 0 allocs/op
BenchmarkRune_ASCII/mattn/go-runewidth-8 264.3 ns/op 484.31 MB/s 0 B/op 0 allocs/op
BenchmarkRune_Emoji/clipperhouse/displaywidth-8 1374 ns/op 527.02 MB/s 0 B/op 0 allocs/op
BenchmarkRune_Emoji/mattn/go-runewidth-8 2210 ns/op 327.66 MB/s 0 B/op 0 allocs/op
BenchmarkStringDefault/displaywidth-8 10537 ns/op 160.10 MB/s 0 B/op 0 allocs/op
BenchmarkStringDefault/go-runewidth-8 14162 ns/op 119.12 MB/s 0 B/op 0 allocs/op
BenchmarkString_EAW/displaywidth-8 10776 ns/op 156.55 MB/s 0 B/op 0 allocs/op
BenchmarkString_EAW/go-runewidth-8 23987 ns/op 70.33 MB/s 0 B/op 0 allocs/op
BenchmarkString_StrictEmoji/displaywidth-8 10892 ns/op 154.88 MB/s 0 B/op 0 allocs/op
BenchmarkString_StrictEmoji/go-runewidth-8 14552 ns/op 115.93 MB/s 0 B/op 0 allocs/op
BenchmarkString_ASCII/displaywidth-8 1116 ns/op 114.72 MB/s 0 B/op 0 allocs/op
BenchmarkString_ASCII/go-runewidth-8 1178 ns/op 108.67 MB/s 0 B/op 0 allocs/op
BenchmarkString_Unicode/displaywidth-8 896.9 ns/op 148.29 MB/s 0 B/op 0 allocs/op
BenchmarkString_Unicode/go-runewidth-8 1434 ns/op 92.72 MB/s 0 B/op 0 allocs/op
BenchmarkStringWidth_Emoji/displaywidth-8 3033 ns/op 238.74 MB/s 0 B/op 0 allocs/op
BenchmarkStringWidth_Emoji/go-runewidth-8 4841 ns/op 149.56 MB/s 0 B/op 0 allocs/op
BenchmarkString_Mixed/displaywidth-8 4064 ns/op 124.74 MB/s 0 B/op 0 allocs/op
BenchmarkString_Mixed/go-runewidth-8 4696 ns/op 107.97 MB/s 0 B/op 0 allocs/op
BenchmarkString_ControlChars/displaywidth-8 320.6 ns/op 102.93 MB/s 0 B/op 0 allocs/op
BenchmarkString_ControlChars/go-runewidth-8 373.8 ns/op 88.28 MB/s 0 B/op 0 allocs/op
BenchmarkRuneDefault/displaywidth-8 335.5 ns/op 411.35 MB/s 0 B/op 0 allocs/op
BenchmarkRuneDefault/go-runewidth-8 681.2 ns/op 202.58 MB/s 0 B/op 0 allocs/op
BenchmarkRuneWidth_EAW/displaywidth-8 146.7 ns/op 374.80 MB/s 0 B/op 0 allocs/op
BenchmarkRuneWidth_EAW/go-runewidth-8 495.6 ns/op 110.98 MB/s 0 B/op 0 allocs/op
BenchmarkRuneWidth_ASCII/displaywidth-8 63.00 ns/op 460.33 MB/s 0 B/op 0 allocs/op
BenchmarkRuneWidth_ASCII/go-runewidth-8 68.90 ns/op 420.91 MB/s 0 B/op 0 allocs/op
```
I use a similar technique in [this grapheme cluster library](https://github.com/clipperhouse/uax29).
## Compatibility
`displaywidth` will mostly give the same outputs as `go-runewidth`, but there are some differences:
- Unicode category Mn (Nonspacing Mark): `displaywidth` will return width 0, `go-runewidth` may return width 1 for some runes.
- Unicode category Cf (Format): `displaywidth` will return width 0, `go-runewidth` may return width 1 for some runes.
- Unicode category Mc (Spacing Mark): `displaywidth` will return width 1, `go-runewidth` may return width 0 for some runes.
- Unicode category Cs (Surrogate): `displaywidth` will return width 0, `go-runewidth` may return width 1 for some runes. Surrogates are not valid UTF-8; some packages may turn them into the replacement character (U+FFFD).
- Unicode category Zl (Line separator): `displaywidth` will return width 0, `go-runewidth` may return width 1.
- Unicode category Zp (Paragraph separator): `displaywidth` will return width 0, `go-runewidth` may return width 1.
- Unicode Noncharacters (U+FFFE and U+FFFF): `displaywidth` will return width 0, `go-runewidth` may return width 1.
See `TestCompatibility` for more details.

View File

@@ -1,72 +0,0 @@
package displaywidth
import (
"github.com/clipperhouse/stringish"
"github.com/clipperhouse/uax29/v2/graphemes"
)
// Graphemes is an iterator over grapheme clusters.
//
// Iterate using the Next method, and get the width of the current grapheme
// using the Width method.
type Graphemes[T stringish.Interface] struct {
iter graphemes.Iterator[T]
options Options
}
// Next advances the iterator to the next grapheme cluster.
func (g *Graphemes[T]) Next() bool {
return g.iter.Next()
}
// Value returns the current grapheme cluster.
func (g *Graphemes[T]) Value() T {
return g.iter.Value()
}
// Width returns the display width of the current grapheme cluster.
func (g *Graphemes[T]) Width() int {
return graphemeWidth(g.Value(), g.options)
}
// StringGraphemes returns an iterator over grapheme clusters for the given
// string.
//
// Iterate using the Next method, and get the width of the current grapheme
// using the Width method.
func StringGraphemes(s string) Graphemes[string] {
return DefaultOptions.StringGraphemes(s)
}
// StringGraphemes returns an iterator over grapheme clusters for the given
// string, with the given options.
//
// Iterate using the Next method, and get the width of the current grapheme
// using the Width method.
func (options Options) StringGraphemes(s string) Graphemes[string] {
return Graphemes[string]{
iter: graphemes.FromString(s),
options: options,
}
}
// BytesGraphemes returns an iterator over grapheme clusters for the given
// []byte.
//
// Iterate using the Next method, and get the width of the current grapheme
// using the Width method.
func BytesGraphemes(s []byte) Graphemes[[]byte] {
return DefaultOptions.BytesGraphemes(s)
}
// BytesGraphemes returns an iterator over grapheme clusters for the given
// []byte, with the given options.
//
// Iterate using the Next method, and get the width of the current grapheme
// using the Width method.
func (options Options) BytesGraphemes(s []byte) Graphemes[[]byte] {
return Graphemes[[]byte]{
iter: graphemes.FromBytes(s),
options: options,
}
}

View File

@@ -1,91 +0,0 @@
package displaywidth
// propertyWidths is a jump table of sorts, instead of a switch
var propertyWidths = [5]int{
_Default: 1,
_Zero_Width: 0,
_East_Asian_Wide: 2,
_East_Asian_Ambiguous: 1,
_Emoji: 2,
}
// asciiWidths is a lookup table for single-byte character widths. Printable
// ASCII characters have width 1, control characters have width 0.
//
// It is intended for valid single-byte UTF-8, which means <128.
//
// If you look up an index >= 128, that is either:
// - invalid UTF-8, or
// - a multi-byte UTF-8 sequence, in which case you should be operating on
// the grapheme cluster, and not using this table
//
// We will return a default value of 1 in those cases, so as not to panic.
var asciiWidths = [256]int8{
// Control characters (0x00-0x1F): width 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// Printable ASCII (0x20-0x7E): width 1
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
// DEL (0x7F): width 0
0,
// >= 128
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
}
// asciiProperties is a lookup table for single-byte character properties.
// It is intended for valid single-byte UTF-8, which means <128.
//
// If you look up an index >= 128, that is either:
// - invalid UTF-8, or
// - a multi-byte UTF-8 sequence, in which case you should be operating on
// the grapheme cluster, and not using this table
//
// We will return a default value of _Default in those cases, so as not to
// panic.
var asciiProperties = [256]property{
// Control characters (0x00-0x1F): _Zero_Width
_Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width,
_Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width,
_Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width,
_Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width,
// Printable ASCII (0x20-0x7E): _Default
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default,
// DEL (0x7F): _Zero_Width
_Zero_Width,
// >= 128
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -7,205 +7,153 @@ import (
"github.com/clipperhouse/uax29/v2/graphemes"
)
// Options allows you to specify the treatment of ambiguous East Asian
// characters. When EastAsianWidth is false (default), ambiguous East Asian
// characters are treated as width 1. When EastAsianWidth is true, ambiguous
// East Asian characters are treated as width 2.
type Options struct {
EastAsianWidth bool
}
// DefaultOptions is the default options for the display width
// calculation, which is EastAsianWidth: false.
var DefaultOptions = Options{EastAsianWidth: false}
// String calculates the display width of a string,
// by iterating over grapheme clusters in the string
// and summing their widths.
// String calculates the display width of a string
// using the [DefaultOptions]
func String(s string) int {
return DefaultOptions.String(s)
}
// String calculates the display width of a string, for the given options, by
// iterating over grapheme clusters in the string and summing their widths.
func (options Options) String(s string) int {
// Optimization: no need to parse grapheme
switch len(s) {
case 0:
return 0
case 1:
return int(asciiWidths[s[0]])
}
width := 0
g := graphemes.FromString(s)
for g.Next() {
width += graphemeWidth(g.Value(), options)
}
return width
}
// Bytes calculates the display width of a []byte,
// by iterating over grapheme clusters in the byte slice
// and summing their widths.
// Bytes calculates the display width of a []byte
// using the [DefaultOptions]
func Bytes(s []byte) int {
return DefaultOptions.Bytes(s)
}
// Bytes calculates the display width of a []byte, for the given options, by
// iterating over grapheme clusters in the slice and summing their widths.
func (options Options) Bytes(s []byte) int {
// Optimization: no need to parse grapheme
switch len(s) {
case 0:
return 0
case 1:
return int(asciiWidths[s[0]])
}
width := 0
g := graphemes.FromBytes(s)
for g.Next() {
width += graphemeWidth(g.Value(), options)
}
return width
}
// Rune calculates the display width of a rune. You
// should almost certainly use [String] or [Bytes] for
// most purposes.
//
// The smallest unit of display width is a grapheme
// cluster, not a rune. Iterating over runes to measure
// width is incorrect in many cases.
func Rune(r rune) int {
return DefaultOptions.Rune(r)
}
// Rune calculates the display width of a rune, for the given options.
//
// You should almost certainly use [String] or [Bytes] for most purposes.
//
// The smallest unit of display width is a grapheme cluster, not a rune.
// Iterating over runes to measure width is incorrect in many cases.
func (options Options) Rune(r rune) int {
if r < utf8.RuneSelf {
return int(asciiWidths[byte(r)])
type Options struct {
EastAsianWidth bool
StrictEmojiNeutral bool
}
var DefaultOptions = Options{
EastAsianWidth: false,
StrictEmojiNeutral: true,
}
// String calculates the display width of a string
// for the given options
func (options Options) String(s string) int {
if len(s) == 0 {
return 0
}
// Surrogates (U+D800-U+DFFF) are invalid UTF-8.
total := 0
g := graphemes.FromString(s)
for g.Next() {
// The first character in the grapheme cluster determines the width;
// modifiers and joiners do not contribute to the width.
props, _ := lookupProperties(g.Value())
total += props.width(options)
}
return total
}
// BytesOptions calculates the display width of a []byte
// for the given options
func (options Options) Bytes(s []byte) int {
if len(s) == 0 {
return 0
}
total := 0
g := graphemes.FromBytes(s)
for g.Next() {
// The first character in the grapheme cluster determines the width;
// modifiers and joiners do not contribute to the width.
props, _ := lookupProperties(g.Value())
total += props.width(options)
}
return total
}
func (options Options) Rune(r rune) int {
// Fast path for ASCII
if r < utf8.RuneSelf {
if isASCIIControl(byte(r)) {
// Control (0x00-0x1F) and DEL (0x7F)
return 0
}
// ASCII printable (0x20-0x7E)
return 1
}
// Surrogates (U+D800-U+DFFF) are invalid UTF-8 and have zero width
// Other packages might turn them into the replacement character (U+FFFD)
// in which case, we won't see it.
if r >= 0xD800 && r <= 0xDFFF {
return 0
}
var buf [4]byte
// Stack-allocated to avoid heap allocation
var buf [4]byte // UTF-8 is at most 4 bytes
n := utf8.EncodeRune(buf[:], r)
// Skip the grapheme iterator
return lookupProperties(buf[:n]).width(options)
// Skip the grapheme iterator and directly lookup properties
props, _ := lookupProperties(buf[:n])
return props.width(options)
}
// graphemeWidth returns the display width of a grapheme cluster.
// The passed string must be a single grapheme cluster.
func graphemeWidth[T stringish.Interface](s T, options Options) int {
// Optimization: no need to look up properties
switch len(s) {
case 0:
return 0
case 1:
return int(asciiWidths[s[0]])
func isASCIIControl(b byte) bool {
return b < 0x20 || b == 0x7F
}
const defaultWidth = 1
// is returns true if the property flag is set
func (p property) is(flag property) bool {
return p&flag != 0
}
// lookupProperties returns the properties for the first character in a string
func lookupProperties[T stringish.Interface](s T) (property, int) {
if len(s) == 0 {
return 0, 0
}
return lookupProperties(s).width(options)
}
// isRIPrefix checks if the slice matches the Regional Indicator prefix
// (F0 9F 87). It assumes len(s) >= 3.
func isRIPrefix[T stringish.Interface](s T) bool {
return s[0] == 0xF0 && s[1] == 0x9F && s[2] == 0x87
}
// isVS16 checks if the slice matches VS16 (U+FE0F) UTF-8 encoding
// (EF B8 8F). It assumes len(s) >= 3.
func isVS16[T stringish.Interface](s T) bool {
return s[0] == 0xEF && s[1] == 0xB8 && s[2] == 0x8F
}
// lookupProperties returns the properties for a grapheme.
// The passed string must be at least one byte long.
//
// Callers must handle zero and single-byte strings upstream, both as an
// optimization, and to reduce the scope of this function.
func lookupProperties[T stringish.Interface](s T) property {
l := len(s)
if s[0] < utf8.RuneSelf {
// Check for variation selector after ASCII (e.g., keycap sequences like 1⃣)
if l >= 4 {
// Subslice may help eliminate bounds checks
vs := s[1:4]
if isVS16(vs) {
// VS16 requests emoji presentation (width 2)
return _Emoji
}
// VS15 (0x8E) requests text presentation but does not affect width,
// in my reading of Unicode TR51. Falls through to _Default.
// Fast path for ASCII characters (single byte)
b := s[0]
if b < utf8.RuneSelf { // Single-byte ASCII
if isASCIIControl(b) {
// Control characters (0x00-0x1F) and DEL (0x7F) - width 0
return _ZeroWidth, 1
}
return asciiProperties[s[0]]
// ASCII printable characters (0x20-0x7E) - width 1
// Return 0 properties, width calculation will default to 1
return 0, 1
}
// Regional indicator pair (flag)
if l >= 8 {
// Subslice may help eliminate bounds checks
ri := s[:8]
// First rune
if isRIPrefix(ri[0:3]) {
b3 := ri[3]
if b3 >= 0xA6 && b3 <= 0xBF {
// Second rune
if isRIPrefix(ri[4:7]) {
b7 := ri[7]
if b7 >= 0xA6 && b7 <= 0xBF {
return _Emoji
}
}
}
}
}
p, sz := lookup(s)
// Variation Selectors
if sz > 0 && l >= sz+3 {
// Subslice may help eliminate bounds checks
vs := s[sz : sz+3]
if isVS16(vs) {
// VS16 requests emoji presentation (width 2)
return _Emoji
}
// VS15 (0x8E) requests text presentation but does not affect width,
// in my reading of Unicode TR51. Falls through to return the base
// character's property.
}
return property(p)
// Use the generated trie for lookup
props, size := lookup(s)
return property(props), size
}
const _Default property = 0
const boundsCheck = property(len(propertyWidths) - 1)
// width determines the display width of a character based on its properties,
// width determines the display width of a character based on its properties
// and configuration options
func (p property) width(options Options) int {
if options.EastAsianWidth && p == _East_Asian_Ambiguous {
if p == 0 {
// Character not in trie, use default behavior
return defaultWidth
}
if p.is(_ZeroWidth) {
return 0
}
if options.EastAsianWidth {
if p.is(_East_Asian_Ambiguous) {
return 2
}
if p.is(_East_Asian_Ambiguous|_Emoji) && !options.StrictEmojiNeutral {
return 2
}
}
if p.is(_East_Asian_Full_Wide) {
return 2
}
// Bounds check may help the compiler eliminate its bounds check,
// and safety of course.
if p > boundsCheck {
return 1 // default width
}
return propertyWidths[p]
// Default width for all other characters
return defaultWidth
}

View File

@@ -1,9 +1,5 @@
An implementation of grapheme cluster boundaries from [Unicode text segmentation](https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries) (UAX 29), for Unicode version 15.0.0.
[![Documentation](https://pkg.go.dev/badge/github.com/clipperhouse/uax29/v2/graphemes.svg)](https://pkg.go.dev/github.com/clipperhouse/uax29/v2/graphemes)
![Tests](https://github.com/clipperhouse/uax29/actions/workflows/gotest.yml/badge.svg)
![Fuzz](https://github.com/clipperhouse/uax29/actions/workflows/gofuzz.yml/badge.svg)
## Quick start
```
@@ -22,14 +18,15 @@ for tokens.Next() { // Next() returns true until end of data
}
```
[![Documentation](https://pkg.go.dev/badge/github.com/clipperhouse/uax29/v2/graphemes.svg)](https://pkg.go.dev/github.com/clipperhouse/uax29/v2/graphemes)
_A grapheme is a “single visible character”, which might be a simple as a single letter, or a complex emoji that consists of several Unicode code points._
## Conformance
We use the Unicode [test suite](https://unicode.org/reports/tr41/tr41-26.html#Tests29).
We use the Unicode [test suite](https://unicode.org/reports/tr41/tr41-26.html#Tests29). Status:
![Tests](https://github.com/clipperhouse/uax29/actions/workflows/gotest.yml/badge.svg)
![Fuzz](https://github.com/clipperhouse/uax29/actions/workflows/gofuzz.yml/badge.svg)
![Go](https://github.com/clipperhouse/uax29/actions/workflows/gotest.yml/badge.svg)
## APIs
@@ -74,18 +71,9 @@ for tokens.Next() { // Next() returns true until end of data
}
```
### Benchmarks
### Performance
On a Mac M2 laptop, we see around 200MB/s, or around 100 million graphemes per second, and no allocations.
```
goos: darwin
goarch: arm64
pkg: github.com/clipperhouse/uax29/graphemes/comparative
cpu: Apple M2
BenchmarkGraphemes/clipperhouse/uax29-8 173805 ns/op 201.16 MB/s 0 B/op 0 allocs/op
BenchmarkGraphemes/rivo/uniseg-8 2045128 ns/op 17.10 MB/s 0 B/op 0 allocs/op
```
On a Mac M2 laptop, we see around 200MB/s, or around 100 million graphemes per second. You should see ~constant memory, and no allocations.
### Invalid inputs

View File

@@ -1,11 +1,8 @@
package graphemes
import (
"github.com/clipperhouse/stringish"
"github.com/clipperhouse/uax29/v2/internal/iterators"
)
import "github.com/clipperhouse/uax29/v2/internal/iterators"
type Iterator[T stringish.Interface] struct {
type Iterator[T iterators.Stringish] struct {
*iterators.Iterator[T]
}

View File

@@ -3,7 +3,7 @@ package graphemes
import (
"bufio"
"github.com/clipperhouse/stringish"
"github.com/clipperhouse/uax29/v2/internal/iterators"
)
// is determines if lookup intersects propert(ies)
@@ -18,7 +18,7 @@ const _Ignore = _Extend
// See https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries.
var SplitFunc bufio.SplitFunc = splitFunc[[]byte]
func splitFunc[T stringish.Interface](data T, atEOF bool) (advance int, token T, err error) {
func splitFunc[T iterators.Stringish](data T, atEOF bool) (advance int, token T, err error) {
var empty T
if len(data) == 0 {
return 0, empty, nil

View File

@@ -1,10 +1,10 @@
package graphemes
import "github.com/clipperhouse/stringish"
// generated by github.com/clipperhouse/uax29/v2
// from https://www.unicode.org/Public/15.0.0/ucd/auxiliary/GraphemeBreakProperty.txt
import "github.com/clipperhouse/uax29/v2/internal/iterators"
type property uint16
const (
@@ -27,7 +27,7 @@ const (
// lookup returns the trie value for the first UTF-8 encoding in s and
// the width in bytes of this encoding. The size will be 0 if s does not
// hold enough bytes to complete the encoding. len(s) must be greater than 0.
func lookup[T stringish.Interface](s T) (v property, sz int) {
func lookup[T iterators.Stringish](s T) (v property, sz int) {
c0 := s[0]
switch {
case c0 < 0x80: // is ASCII

View File

@@ -1,12 +1,14 @@
package iterators
import "github.com/clipperhouse/stringish"
type Stringish interface {
[]byte | string
}
type SplitFunc[T stringish.Interface] func(T, bool) (int, T, error)
type SplitFunc[T Stringish] func(T, bool) (int, T, error)
// Iterator is a generic iterator for words that are either []byte or string.
// Iterate while Next() is true, and access the word via Value().
type Iterator[T stringish.Interface] struct {
type Iterator[T Stringish] struct {
split SplitFunc[T]
data T
start int
@@ -14,7 +16,7 @@ type Iterator[T stringish.Interface] struct {
}
// New creates a new Iterator for the given data and SplitFunc.
func New[T stringish.Interface](split SplitFunc[T], data T) *Iterator[T] {
func New[T Stringish](split SplitFunc[T], data T) *Iterator[T] {
return &Iterator[T]{
split: split,
data: data,
@@ -81,20 +83,3 @@ func (iter *Iterator[T]) Reset() {
iter.start = 0
iter.pos = 0
}
func (iter *Iterator[T]) First() T {
if len(iter.data) == 0 {
return iter.data
}
advance, _, err := iter.split(iter.data, true)
if err != nil {
panic(err)
}
if advance <= 0 {
panic("SplitFunc returned a zero or negative advance")
}
if advance > len(iter.data) {
panic("SplitFunc advanced beyond the end of the data")
}
return iter.data[:advance]
}

View File

@@ -657,13 +657,6 @@ func Mark(names ...string) {
// It is similar to Dbg but formats the output as JSON for better readability. It is thread-safe and respects
// the loggers configuration (e.g., enabled, level, suspend, handler, middleware).
func Output(values ...interface{}) {
defaultLogger.output(2, values...)
}
// Inspect logs one or more values in a **developer-friendly, deeply introspective format** at Info level.
// It includes the caller file and line number, and reveals **all fields** — including:
func Inspect(values ...interface{}) {
o := NewInspector(defaultLogger)
o.Log(2, values...)
}

View File

@@ -79,7 +79,7 @@ func (o *Inspector) Log(skip int, values ...interface{}) {
}
// Construct log message with file, line, and JSON data
msg := fmt.Sprintf("[%s:%d] INSPECT: %s", shortFile, line, string(jsonData))
msg := fmt.Sprintf("[%s:%d] DUMP: %s", shortFile, line, string(jsonData))
o.logger.log(lx.LevelInfo, lx.ClassText, msg, nil, false)
}
}

View File

@@ -350,58 +350,17 @@ func (l *Logger) Dump(values ...interface{}) {
}
}
// Output logs each value as pretty-printed JSON for REST debugging.
// Each value is logged on its own line with [file:line] and a blank line after the header.
// Ideal for inspecting outgoing/incoming REST payloads.
// Output logs data in a human-readable JSON format at Info level, including caller file and line information.
// It is similar to Dbg but formats the output as JSON for better readability. It is thread-safe and respects
// the logger's configuration (e.g., enabled, level, suspend, handler, middleware).
// Example:
//
// logger := New("app").Enable()
// x := map[string]int{"key": 42}
// logger.Output(x) // Output: [app] INFO: [file.go:123] JSON: {"key": 42}
//
// Logger method to provide access to Output functionality
func (l *Logger) Output(values ...interface{}) {
l.output(2, values...)
}
func (l *Logger) output(skip int, values ...interface{}) {
if !l.shouldLog(lx.LevelInfo) {
return
}
_, file, line, ok := runtime.Caller(skip)
if !ok {
return
}
shortFile := file
if idx := strings.LastIndex(file, "/"); idx >= 0 {
shortFile = file[idx+1:]
}
header := fmt.Sprintf("[%s:%d] JSON:\n", shortFile, line)
for _, v := range values {
// Always pretty-print with indent
b, err := json.MarshalIndent(v, " ", " ")
if err != nil {
b, _ = json.MarshalIndent(map[string]any{
"value": fmt.Sprintf("%+v", v),
"error": err.Error(),
}, " ", " ")
}
l.log(lx.LevelInfo, lx.ClassText, header+string(b), nil, false)
}
}
// Inspect logs one or more values in a **developer-friendly, deeply introspective format** at Info level.
// It includes the caller file and line number, and reveals **all fields** — including:
//
// - Private (unexported) fields → prefixed with `(field)`
// - Embedded structs (inlined)
// - Pointers and nil values → shown as `*(field)` or `nil`
// - Full struct nesting and type information
//
// This method uses `NewInspector` under the hood, which performs **full reflection-based traversal**.
// It is **not** meant for production logging or REST APIs — use `Output` for that.
//
// Ideal for:
// - Debugging complex internal state
// - Inspecting structs with private fields
// - Understanding struct embedding and pointer behavior
func (l *Logger) Inspect(values ...interface{}) {
o := NewInspector(l)
o.Log(2, values...)
}

View File

@@ -28,7 +28,7 @@ go get github.com/olekukonko/tablewriter@v0.0.5
#### Latest Version
The latest stable version
```bash
go get github.com/olekukonko/tablewriter@v1.1.2
go get github.com/olekukonko/tablewriter@v1.1.1
```
**Warning:** Version `v1.0.0` contains missing functionality and should not be used.
@@ -62,7 +62,7 @@ func main() {
data := [][]string{
{"Package", "Version", "Status"},
{"tablewriter", "v0.0.5", "legacy"},
{"tablewriter", "v1.1.2", "latest"},
{"tablewriter", "v1.1.1", "latest"},
}
table := tablewriter.NewWriter(os.Stdout)
@@ -77,7 +77,7 @@ func main() {
│ PACKAGE │ VERSION │ STATUS │
├─────────────┼─────────┼────────┤
│ tablewriter │ v0.0.5 │ legacy │
│ tablewriter │ v1.1.2 │ latest │
│ tablewriter │ v1.1.1 │ latest │
└─────────────┴─────────┴────────┘
```

View File

@@ -1,8 +1,6 @@
package tablewriter
import (
"github.com/mattn/go-runewidth"
"github.com/olekukonko/tablewriter/pkg/twwidth"
"github.com/olekukonko/tablewriter/tw"
)
@@ -220,16 +218,3 @@ func WithTableMax(width int) Option {
}
}
}
// Deprecated: use WithEastAsian instead.
// WithCondition provides a way to set a custom global runewidth.Condition
// that will be used for all subsequent display width calculations by the twwidth (twdw) package.
//
// The runewidth.Condition object allows for more fine-grained control over how rune widths
// are determined, beyond just toggling EastAsianWidth. This could include settings for
// ambiguous width characters or other future properties of runewidth.Condition.
func WithCondition(cond *runewidth.Condition) Option {
return func(target *Table) {
twwidth.SetCondition(cond)
}
}

View File

@@ -3,8 +3,8 @@ package tablewriter
import (
"reflect"
"github.com/mattn/go-runewidth"
"github.com/olekukonko/ll"
"github.com/olekukonko/tablewriter/pkg/twcache"
"github.com/olekukonko/tablewriter/pkg/twwidth"
"github.com/olekukonko/tablewriter/tw"
)
@@ -471,48 +471,22 @@ func WithStreaming(c tw.StreamConfig) Option {
func WithStringer(stringer interface{}) Option {
return func(t *Table) {
t.stringer = stringer
t.stringerCache = twcache.NewLRU[reflect.Type, reflect.Value](tw.DefaultCacheStringCapacity)
t.stringerCacheMu.Lock()
t.stringerCache = make(map[reflect.Type]reflect.Value)
t.stringerCacheMu.Unlock()
if t.logger != nil {
t.logger.Debug("Stringer updated, cache cleared")
}
}
}
// WithStringerCache enables the default LRU caching for the stringer function.
// It initializes the cache with a default capacity if one does not already exist.
// WithStringerCache enables caching for the stringer function.
// Logs the change if debugging is enabled.
func WithStringerCache() Option {
return func(t *Table) {
// Initialize default cache if strictly necessary (nil),
// or if you want to ensure the default implementation is used.
if t.stringerCache == nil {
// NewLRU returns (Instance, error). We ignore the error here assuming capacity > 0.
cache := twcache.NewLRU[reflect.Type, reflect.Value](tw.DefaultCacheStringCapacity)
t.stringerCache = cache
}
t.stringerCacheEnabled = true
if t.logger != nil {
t.logger.Debug("Option: WithStringerCache enabled (Default LRU)")
}
}
}
// WithStringerCacheCustom enables caching for the stringer function using a specific implementation.
// Passing nil disables caching entirely.
func WithStringerCacheCustom(cache twcache.Cache[reflect.Type, reflect.Value]) Option {
return func(t *Table) {
if cache == nil {
t.stringerCache = nil
if t.logger != nil {
t.logger.Debug("Option: WithStringerCacheCustom called with nil (Caching Disabled)")
}
return
}
// Set the custom cache and enable the flag
t.stringerCache = cache
if t.logger != nil {
t.logger.Debug("Option: WithStringerCacheCustom enabled")
t.logger.Debug("Option: WithStringerCache enabled")
}
}
}
@@ -655,20 +629,27 @@ func WithRendition(rendition tw.Rendition) Option {
}
// WithEastAsian configures the global East Asian width calculation setting.
// - state=tw.On: Enables East Asian width calculations. CJK and ambiguous characters
// - enable=true: Enables East Asian width calculations. CJK and ambiguous characters
// are typically measured as double width.
// - state=tw.Off: Disables East Asian width calculations. Characters are generally
// - enable=false: Disables East Asian width calculations. Characters are generally
// measured as single width, subject to Unicode standards.
//
// This setting affects all subsequent display width calculations using the twdw package.
func WithEastAsian(state tw.State) Option {
func WithEastAsian(enable bool) Option {
return func(target *Table) {
if state.Enabled() {
twwidth.SetEastAsian(true)
}
if state.Disabled() {
twwidth.SetEastAsian(false)
}
twwidth.SetEastAsian(enable)
}
}
// WithCondition provides a way to set a custom global runewidth.Condition
// that will be used for all subsequent display width calculations by the twwidth (twdw) package.
//
// The runewidth.Condition object allows for more fine-grained control over how rune widths
// are determined, beyond just toggling EastAsianWidth. This could include settings for
// ambiguous width characters or other future properties of runewidth.Condition.
func WithCondition(cond *runewidth.Condition) Option {
return func(target *Table) {
twwidth.SetCondition(cond)
}
}

View File

@@ -1,12 +0,0 @@
package twcache
// Cache defines a generic interface for a key-value storage with type constraints on keys and values.
// The keys must be of a type that supports comparison.
// Add inserts a new key-value pair, potentially evicting an item if necessary.
// Get retrieves a value associated with the given key, returning a boolean to indicate if the key was found.
// Purge clears all items from the cache.
type Cache[K comparable, V any] interface {
Add(key K, value V) (evicted bool)
Get(key K) (value V, ok bool)
Purge()
}

View File

@@ -1,289 +0,0 @@
package twcache
import (
"sync"
"sync/atomic"
)
// EvictCallback is a function called when an entry is evicted.
// This includes evictions during Purge or Resize operations.
type EvictCallback[K comparable, V any] func(key K, value V)
// LRU is a thread-safe, generic LRU cache with a fixed size.
// It has zero dependencies, high performance, and full features.
type LRU[K comparable, V any] struct {
size int
items map[K]*entry[K, V]
head *entry[K, V] // Most Recently Used
tail *entry[K, V] // Least Recently Used
onEvict EvictCallback[K, V]
mu sync.Mutex
hits atomic.Int64
misses atomic.Int64
}
// entry represents a single item in the LRU linked list.
// It holds the key, value, and pointers to prev/next entries.
type entry[K comparable, V any] struct {
key K
value V
prev *entry[K, V]
next *entry[K, V]
}
// NewLRU creates a new LRU cache with the given size.
// Returns nil if size <= 0, acting as a disabled cache.
// Caps size at 100,000 for reasonableness.
func NewLRU[K comparable, V any](size int) *LRU[K, V] {
return NewLRUEvict[K, V](size, nil)
}
// NewLRUEvict creates a new LRU cache with an eviction callback.
// The callback is optional and called on evictions.
// Returns nil if size <= 0.
func NewLRUEvict[K comparable, V any](size int, onEvict EvictCallback[K, V]) *LRU[K, V] {
if size <= 0 {
return nil // nil = disabled cache (fast path in hot code)
}
if size > 100_000 {
size = 100_000 // reasonable upper bound
}
return &LRU[K, V]{
size: size,
items: make(map[K]*entry[K, V], size),
onEvict: onEvict,
}
}
// GetOrCompute retrieves a value or computes it if missing.
// Ensures no double computation under concurrency.
// Ideal for expensive computations like twwidth.
func (c *LRU[K, V]) GetOrCompute(key K, compute func() V) V {
if c == nil || c.size <= 0 {
return compute()
}
c.mu.Lock()
if e, ok := c.items[key]; ok {
c.moveToFront(e)
c.hits.Add(1)
c.mu.Unlock()
return e.value
}
c.misses.Add(1)
value := compute() // expensive work only on real miss
// Double-check: someone might have added it while computing
if e, ok := c.items[key]; ok {
e.value = value
c.moveToFront(e)
c.mu.Unlock()
return value
}
// Evict if needed
if len(c.items) >= c.size {
c.removeOldest()
}
e := &entry[K, V]{key: key, value: value}
c.addToFront(e)
c.items[key] = e
c.mu.Unlock()
return value
}
// Get retrieves a value by key if it exists.
// Returns the value and true if found, else zero and false.
// Updates the entry to most recently used.
func (c *LRU[K, V]) Get(key K) (V, bool) {
if c == nil || c.size <= 0 {
var zero V
return zero, false
}
c.mu.Lock()
defer c.mu.Unlock()
e, ok := c.items[key]
if !ok {
c.misses.Add(1)
var zero V
return zero, false
}
c.hits.Add(1)
c.moveToFront(e)
return e.value, true
}
// Add inserts or updates a key-value pair.
// Evicts the oldest if cache is full.
// Returns true if an eviction occurred.
func (c *LRU[K, V]) Add(key K, value V) (evicted bool) {
if c == nil || c.size <= 0 {
return false
}
c.mu.Lock()
defer c.mu.Unlock()
if e, ok := c.items[key]; ok {
e.value = value
c.moveToFront(e)
return false
}
if len(c.items) >= c.size {
c.removeOldest()
evicted = true
}
e := &entry[K, V]{key: key, value: value}
c.addToFront(e)
c.items[key] = e
return evicted
}
// Remove deletes a key from the cache.
// Returns true if the key was found and removed.
func (c *LRU[K, V]) Remove(key K) bool {
if c == nil || c.size <= 0 {
return false
}
c.mu.Lock()
defer c.mu.Unlock()
e, ok := c.items[key]
if !ok {
return false
}
c.removeNode(e)
delete(c.items, key)
return true
}
// Purge clears all entries from the cache.
// Calls onEvict for each entry if set.
// Resets hit/miss counters.
func (c *LRU[K, V]) Purge() {
if c == nil || c.size <= 0 {
return
}
c.mu.Lock()
if c.onEvict != nil {
for key, e := range c.items {
c.onEvict(key, e.value)
}
}
c.items = make(map[K]*entry[K, V], c.size)
c.head = nil
c.tail = nil
c.hits.Store(0)
c.misses.Store(0)
c.mu.Unlock()
}
// Len returns the current number of items in the cache.
func (c *LRU[K, V]) Len() int {
if c == nil || c.size <= 0 {
return 0
}
c.mu.Lock()
n := len(c.items)
c.mu.Unlock()
return n
}
// Cap returns the maximum capacity of the cache.
func (c *LRU[K, V]) Cap() int {
if c == nil {
return 0
}
return c.size
}
// HitRate returns the cache hit ratio (0.0 to 1.0).
// Based on hits / (hits + misses).
func (c *LRU[K, V]) HitRate() float64 {
h := c.hits.Load()
m := c.misses.Load()
total := h + m
if total == 0 {
return 0.0
}
return float64(h) / float64(total)
}
// RemoveOldest removes and returns the least recently used item.
// Returns key, value, and true if an item was removed.
// Calls onEvict if set.
func (c *LRU[K, V]) RemoveOldest() (key K, value V, ok bool) {
if c == nil || c.size <= 0 {
return
}
c.mu.Lock()
defer c.mu.Unlock()
if c.tail == nil {
return
}
key = c.tail.key
value = c.tail.value
c.removeOldest()
return key, value, true
}
// moveToFront moves an entry to the front (MRU position).
func (c *LRU[K, V]) moveToFront(e *entry[K, V]) {
if c.head == e {
return
}
c.removeNode(e)
c.addToFront(e)
}
// addToFront adds an entry to the front of the list.
func (c *LRU[K, V]) addToFront(e *entry[K, V]) {
e.prev = nil
e.next = c.head
if c.head != nil {
c.head.prev = e
}
c.head = e
if c.tail == nil {
c.tail = e
}
}
// removeNode removes an entry from the linked list.
func (c *LRU[K, V]) removeNode(e *entry[K, V]) {
if e.prev != nil {
e.prev.next = e.next
} else {
c.head = e.next
}
if e.next != nil {
e.next.prev = e.prev
} else {
c.tail = e.prev
}
e.prev = nil
e.next = nil
}
// removeOldest removes the tail entry (LRU).
// Calls onEvict if set and deletes from map.
func (c *LRU[K, V]) removeOldest() {
if c.tail == nil {
return
}
e := c.tail
if c.onEvict != nil {
c.onEvict(e.key, e.value)
}
c.removeNode(e)
delete(c.items, e.key)
}

View File

@@ -8,53 +8,42 @@ import (
"github.com/clipperhouse/displaywidth"
"github.com/mattn/go-runewidth"
"github.com/olekukonko/tablewriter/pkg/twcache"
)
const (
cacheCapacity = 8192
cachePrefix = "0:"
cacheEastAsianPrefix = "1:"
)
// Options allows for configuring width calculation on a per-call basis.
type Options struct {
EastAsianWidth bool
}
// globalOptions holds the global displaywidth configuration, including East Asian width settings.
var globalOptions Options
var globalOptions displaywidth.Options
// mu protects access to globalOptions for thread safety.
// mu protects access to condition and widthCache for thread safety.
var mu sync.Mutex
// widthCache stores memoized results of Width calculations to improve performance.
var widthCache *twcache.LRU[string, int]
// ansi is a compiled regular expression for stripping ANSI escape codes from strings.
var ansi = Filter()
func init() {
// Initialize global options by detecting from the environment,
// which is the one key feature we get from go-runewidth.
cond := runewidth.NewCondition()
globalOptions = Options{
EastAsianWidth: cond.EastAsianWidth,
}
widthCache = twcache.NewLRU[string, int](cacheCapacity)
globalOptions = newOptions()
widthCache = make(map[cacheKey]int)
}
// makeCacheKey generates a string key for the LRU cache from the input string
// and the current East Asian width setting.
// Prefix "0:" for false, "1:" for true.
func makeCacheKey(str string, eastAsianWidth bool) string {
if eastAsianWidth {
return cacheEastAsianPrefix + str
func newOptions() displaywidth.Options {
// go-runewidth has default logic based on env variables and locale,
// we want to keep that compatibility
cond := runewidth.NewCondition()
options := displaywidth.Options{
EastAsianWidth: cond.EastAsianWidth,
StrictEmojiNeutral: cond.StrictEmojiNeutral,
}
return cachePrefix + str
return options
}
// cacheKey is used as a key for memoizing string width results in widthCache.
type cacheKey struct {
str string // Input string
eastAsianWidth bool // East Asian width setting
}
// widthCache stores memoized results of Width calculations to improve performance.
var widthCache map[cacheKey]int
// Filter compiles and returns a regular expression for matching ANSI escape sequences,
// including CSI (Control Sequence Introducer) and OSC (Operating System Command) sequences.
// The returned regex can be used to strip ANSI codes from strings.
@@ -73,25 +62,20 @@ func Filter() *regexp.Regexp {
return regexp.MustCompile("(" + regCSI + "|" + regOSC + ")")
}
// SetOptions sets the global options for width calculation.
// This function is thread-safe.
func SetOptions(opts Options) {
mu.Lock()
defer mu.Unlock()
if globalOptions.EastAsianWidth != opts.EastAsianWidth {
globalOptions = opts
widthCache.Purge()
}
}
// SetEastAsian enables or disables East Asian width handling globally.
// SetEastAsian enables or disables East Asian width handling for width calculations.
// When the setting changes, the width cache is cleared to ensure accuracy.
// This function is thread-safe.
//
// Example:
//
// twdw.SetEastAsian(true) // Enable East Asian width handling
func SetEastAsian(enable bool) {
SetOptions(Options{EastAsianWidth: enable})
mu.Lock()
defer mu.Unlock()
if globalOptions.EastAsianWidth != enable {
globalOptions.EastAsianWidth = enable
widthCache = make(map[cacheKey]int) // Clear cache on setting change
}
}
// IsEastAsian returns the current East Asian width setting.
@@ -108,67 +92,85 @@ func IsEastAsian() bool {
return globalOptions.EastAsianWidth
}
// Deprecated: use SetOptions with the new twwidth.Options struct instead.
// This function is kept for backward compatibility.
// SetCondition updates the global runewidth.Condition used for width calculations.
// This method is kept for backward compatibility. The condition is converted to
// displaywidth.Options internally for better performance.
func SetCondition(cond *runewidth.Condition) {
mu.Lock()
defer mu.Unlock()
newEastAsianWidth := cond.EastAsianWidth
if globalOptions.EastAsianWidth != newEastAsianWidth {
globalOptions.EastAsianWidth = newEastAsianWidth
widthCache.Purge()
widthCache = make(map[cacheKey]int) // Clear cache on setting change
globalOptions = conditionToOptions(cond)
}
// Convert runewidth.Condition to displaywidth.Options
func conditionToOptions(cond *runewidth.Condition) displaywidth.Options {
return displaywidth.Options{
EastAsianWidth: cond.EastAsianWidth,
StrictEmojiNeutral: cond.StrictEmojiNeutral,
}
}
// Width calculates the visual width of a string using the global cache for performance.
// It excludes ANSI escape sequences and accounts for the global East Asian width setting.
// Width calculates the visual width of a string, excluding ANSI escape sequences,
// using the go-runewidth package for accurate Unicode handling. It accounts for the
// current East Asian width setting and caches results for performance.
// This function is thread-safe.
//
// Example:
//
// width := twdw.Width("Hello\x1b[31mWorld") // Returns 10
func Width(str string) int {
currentEA := IsEastAsian()
key := makeCacheKey(str, currentEA)
if w, found := widthCache.Get(key); found {
mu.Lock()
key := cacheKey{str: str, eastAsianWidth: globalOptions.EastAsianWidth}
if w, found := widthCache[key]; found {
mu.Unlock()
return w
}
mu.Unlock()
options := newOptions()
options.EastAsianWidth = key.eastAsianWidth
opts := displaywidth.Options{EastAsianWidth: currentEA}
stripped := ansi.ReplaceAllLiteralString(str, "")
calculatedWidth := opts.String(stripped)
calculatedWidth := options.String(stripped)
mu.Lock()
widthCache[key] = calculatedWidth
mu.Unlock()
widthCache.Add(key, calculatedWidth)
return calculatedWidth
}
// WidthWithOptions calculates the visual width of a string with specific options,
// bypassing the global settings and cache. This is useful for one-shot calculations
// where global state is not desired.
func WidthWithOptions(str string, opts Options) int {
dwOpts := displaywidth.Options{EastAsianWidth: opts.EastAsianWidth}
stripped := ansi.ReplaceAllLiteralString(str, "")
return dwOpts.String(stripped)
}
// WidthNoCache calculates the visual width of a string without using the global cache.
// WidthNoCache calculates the visual width of a string without using or
// updating the global cache. It uses the current global East Asian width setting.
// This function is intended for internal use (e.g., benchmarking) and is thread-safe.
//
// Example:
//
// width := twdw.WidthNoCache("Hello\x1b[31mWorld") // Returns 10
func WidthNoCache(str string) int {
// This function's behavior is equivalent to a one-shot calculation
// using the current global options. The WidthWithOptions function
// does not interact with the cache, thus fulfilling the requirement.
return WidthWithOptions(str, Options{EastAsianWidth: IsEastAsian()})
mu.Lock()
currentEA := globalOptions.EastAsianWidth
mu.Unlock()
options := newOptions()
options.EastAsianWidth = currentEA
stripped := ansi.ReplaceAllLiteralString(str, "")
return options.String(stripped)
}
// Deprecated: use WidthWithOptions with the new twwidth.Options struct instead.
// This function is kept for backward compatibility.
// Display calculates the visual width of a string, excluding ANSI escape sequences,
// using the provided runewidth condition. Unlike Width, it does not use caching
// and is intended for cases where a specific condition is required.
// This function is thread-safe with respect to the provided condition.
//
// Example:
//
// cond := runewidth.NewCondition()
// width := twdw.Display(cond, "Hello\x1b[31mWorld") // Returns 10
func Display(cond *runewidth.Condition, str string) int {
opts := Options{EastAsianWidth: cond.EastAsianWidth}
return WidthWithOptions(str, opts)
options := conditionToOptions(cond)
return options.String(ansi.ReplaceAllLiteralString(str, ""))
}
// Truncate shortens a string to fit within a specified visual width, optionally
@@ -215,34 +217,31 @@ func Truncate(s string, maxWidth int, suffix ...string) string {
// Case 3: String fits completely or fits with suffix.
// Here, maxWidth is the total budget for the line.
if sDisplayWidth <= maxWidth {
// If the string contains ANSI, we must ensure it ends with a reset
// to prevent bleeding, even if we don't truncate.
safeS := s
if strings.Contains(s, "\x1b") && !strings.HasSuffix(s, "\x1b[0m") {
safeS += "\x1b[0m"
}
if len(suffixStr) == 0 { // No suffix.
return safeS
return s
}
// Suffix is provided. Check if s + suffix fits.
if sDisplayWidth+suffixDisplayWidth <= maxWidth {
return safeS + suffixStr
return s + suffixStr
}
// s fits, but s + suffix is too long. Return s (with reset if needed).
return safeS
// s fits, but s + suffix is too long. Return s.
return s
}
// Case 4: String needs truncation (sDisplayWidth > maxWidth).
// maxWidth is the total budget for the final string (content + suffix).
currentGlobalEastAsianWidth := IsEastAsian()
// Capture the global EastAsianWidth setting once for consistent use
mu.Lock()
currentGlobalEastAsianWidth := globalOptions.EastAsianWidth
mu.Unlock()
// Special case for EastAsian true: if only suffix fits, return suffix.
// This was derived from previous test behavior.
if len(suffixStr) > 0 && currentGlobalEastAsianWidth {
provisionalContentWidth := maxWidth - suffixDisplayWidth
if provisionalContentWidth == 0 { // Exactly enough space for suffix only
return suffixStr
return suffixStr // <<<< MODIFIED: No ANSI reset here
}
}
@@ -264,6 +263,7 @@ func Truncate(s string, maxWidth int, suffix ...string) string {
}
return "" // Cannot fit anything.
}
// If targetContentForIteration is 0, loop won't run, result will be empty string, then suffix is added.
var contentBuf bytes.Buffer
var currentContentDisplayWidth int
@@ -271,7 +271,8 @@ func Truncate(s string, maxWidth int, suffix ...string) string {
inAnsiSequence := false
ansiWrittenToContent := false
dwOpts := displaywidth.Options{EastAsianWidth: currentGlobalEastAsianWidth}
options := newOptions()
options.EastAsianWidth = currentGlobalEastAsianWidth
for _, r := range s {
if r == '\x1b' {
@@ -305,7 +306,7 @@ func Truncate(s string, maxWidth int, suffix ...string) string {
ansiSeqBuf.Reset()
}
} else { // Normal character
runeDisplayWidth := dwOpts.Rune(r)
runeDisplayWidth := options.Rune(r)
if targetContentForIteration == 0 { // No budget for content at all
break
}
@@ -319,51 +320,32 @@ func Truncate(s string, maxWidth int, suffix ...string) string {
result := contentBuf.String()
// Determine if we need to append a reset sequence to prevent color bleeding.
// This is needed if we wrote any ANSI codes or if the input had active codes
// that we might have cut off or left open.
needsReset := false
if (ansiWrittenToContent || (inAnsiSequence && strings.Contains(s, "\x1b["))) && (currentContentDisplayWidth > 0 || ansiWrittenToContent) {
if !strings.HasSuffix(result, "\x1b[0m") {
// Suffix is added if:
// 1. A suffix string is provided.
// 2. Truncation actually happened (sDisplayWidth > maxWidth originally)
// OR if the content part is empty but a suffix is meant to be shown
// (e.g. targetContentForIteration was 0).
if len(suffixStr) > 0 {
// Add suffix if we are in the truncation path (sDisplayWidth > maxWidth)
// OR if targetContentForIteration was 0 (meaning only suffix should be shown)
// but we must ensure we don't exceed original maxWidth.
// The logic above for targetContentForIteration already ensures space.
needsReset := false
// Condition for reset: if styling was active in 's' and might affect suffix
if (ansiWrittenToContent || (inAnsiSequence && strings.Contains(s, "\x1b["))) && (currentContentDisplayWidth > 0 || ansiWrittenToContent) {
if !strings.HasSuffix(result, "\x1b[0m") {
needsReset = true
}
} else if currentContentDisplayWidth > 0 && strings.Contains(result, "\x1b[") && !strings.HasSuffix(result, "\x1b[0m") && strings.Contains(s, "\x1b[") {
// If result has content and ANSI, and original had ANSI, and result not already reset
needsReset = true
}
} else if currentContentDisplayWidth > 0 && strings.Contains(result, "\x1b[") && !strings.HasSuffix(result, "\x1b[0m") && strings.Contains(s, "\x1b[") {
needsReset = true
}
if needsReset {
result += "\x1b[0m"
}
// Suffix is added if provided.
if len(suffixStr) > 0 {
if needsReset {
result += "\x1b[0m"
}
result += suffixStr
}
return result
}
// SetCacheCapacity changes the cache size dynamically
// If capacity <= 0, disables caching entirely
func SetCacheCapacity(capacity int) {
mu.Lock()
defer mu.Unlock()
if capacity <= 0 {
widthCache = nil // nil = fully disabled
return
}
newCache := twcache.NewLRU[string, int](capacity)
widthCache = newCache
}
// GetCacheStats returns current cache statistics
func GetCacheStats() (size, capacity int, hitRate float64) {
mu.Lock()
defer mu.Unlock()
if widthCache == nil {
return 0, 0, 0
}
return widthCache.Len(), widthCache.Cap(), widthCache.HitRate()
}

View File

@@ -8,11 +8,11 @@ import (
"reflect"
"runtime"
"strings"
"sync"
"github.com/olekukonko/errors"
"github.com/olekukonko/ll"
"github.com/olekukonko/ll/lh"
"github.com/olekukonko/tablewriter/pkg/twcache"
"github.com/olekukonko/tablewriter/pkg/twwarp"
"github.com/olekukonko/tablewriter/pkg/twwidth"
"github.com/olekukonko/tablewriter/renderer"
@@ -52,7 +52,9 @@ type Table struct {
streamRowCounter int // Counter for rows rendered in streaming mode (0-indexed logical rows)
// cache
stringerCache twcache.Cache[reflect.Type, reflect.Value] // Cache for stringer reflection
stringerCache map[reflect.Type]reflect.Value // Cache for stringer reflection
stringerCacheMu sync.RWMutex // Mutex for thread-safe cache access
stringerCacheEnabled bool // Flag to enable/disable caching
batchRenderNumCols int
isBatchRenderNumColsSet bool
@@ -124,7 +126,8 @@ func NewTable(w io.Writer, opts ...Option) *Table {
streamRowCounter: 0,
// Cache
stringerCache: twcache.NewLRU[reflect.Type, reflect.Value](tw.DefaultCacheStringCapacity),
stringerCache: make(map[reflect.Type]reflect.Value),
stringerCacheEnabled: false, // Disabled by default
}
// set Options
@@ -480,11 +483,10 @@ func (t *Table) Reset() {
t.streamRowCounter = 0
// The stringer and its cache are part of the table's configuration,
if t.stringerCache == nil {
t.stringerCache = twcache.NewLRU[reflect.Type, reflect.Value](tw.DefaultCacheStringCapacity)
t.logger.Debug("Reset(): Stringer cache reset to default capacity.")
} else {
t.stringerCache.Purge()
if t.stringerCacheEnabled {
t.stringerCacheMu.Lock()
t.stringerCache = make(map[reflect.Type]reflect.Value)
t.stringerCacheMu.Unlock()
t.logger.Debug("Reset(): Stringer cache cleared.")
}

View File

@@ -8,8 +8,6 @@ const (
Success = 1 // Operation succeeded
MinimumColumnWidth = 8
DefaultCacheStringCapacity = 10 * 1024 // 10 KB
)
const (

View File

@@ -1064,13 +1064,17 @@ func (t *Table) convertToStringer(input interface{}) ([]string, error) {
t.logger.Debugf("convertToString attempt %v using %v", input, t.stringer)
inputType := reflect.TypeOf(input)
stringerFuncVal := reflect.ValueOf(t.stringer)
stringerFuncType := stringerFuncVal.Type()
// Cache lookup using twcache.LRU
// This assumes t.stringerCache is *twcache.LRU[reflect.Type, reflect.Value]
if t.stringerCache != nil {
if cachedFunc, ok := t.stringerCache.Get(inputType); ok {
// Cache lookup (simplified, actual cache logic can be more complex)
if t.stringerCacheEnabled {
t.stringerCacheMu.RLock()
cachedFunc, ok := t.stringerCache[inputType]
t.stringerCacheMu.RUnlock()
if ok {
// Add proper type checking for cachedFunc against input here if necessary
t.logger.Debugf("convertToStringer: Cache hit for type %v", inputType)
// We can proceed to call it immediately because it's already been validated/cached
results := cachedFunc.Call([]reflect.Value{reflect.ValueOf(input)})
if len(results) == 1 && results[0].Type() == reflect.TypeOf([]string{}) {
return results[0].Interface().([]string), nil
@@ -1078,9 +1082,6 @@ func (t *Table) convertToStringer(input interface{}) ([]string, error) {
}
}
stringerFuncVal := reflect.ValueOf(t.stringer)
stringerFuncType := stringerFuncVal.Type()
// Robust type checking for the stringer function
validSignature := stringerFuncVal.Kind() == reflect.Func &&
stringerFuncType.NumIn() == 1 &&
@@ -1104,6 +1105,10 @@ func (t *Table) convertToStringer(input interface{}) ([]string, error) {
}
} else if paramType.Kind() == reflect.Interface || (paramType.Kind() == reflect.Ptr && paramType.Elem().Kind() != reflect.Interface) {
// If input is nil, it can be assigned if stringer expects an interface or a pointer type
// (but not a pointer to an interface, which is rare for stringers).
// A nil value for a concrete type parameter would cause a panic on Call.
// So, if paramType is not an interface/pointer, and input is nil, it's an issue.
// This needs careful handling. For now, assume assignable if interface/pointer.
assignable = true
}
@@ -1115,6 +1120,7 @@ func (t *Table) convertToStringer(input interface{}) ([]string, error) {
if input == nil {
// If input is nil, we must pass a zero value of the stringer's parameter type
// if that type is a pointer or interface.
// Passing reflect.ValueOf(nil) directly will cause issues if paramType is concrete.
callArgs = []reflect.Value{reflect.Zero(paramType)}
} else {
callArgs = []reflect.Value{reflect.ValueOf(input)}
@@ -1122,9 +1128,10 @@ func (t *Table) convertToStringer(input interface{}) ([]string, error) {
resultValues := stringerFuncVal.Call(callArgs)
// Add to cache if enabled (not nil) and input type is valid
if t.stringerCache != nil && inputType != nil {
t.stringerCache.Add(inputType, stringerFuncVal)
if t.stringerCacheEnabled && inputType != nil { // Only cache if inputType is valid
t.stringerCacheMu.Lock()
t.stringerCache[inputType] = stringerFuncVal
t.stringerCacheMu.Unlock()
}
return resultValues[0].Interface().([]string), nil

View File

@@ -1,14 +1,3 @@
## 1.39.0
### Features
Add `MatchErrorStrictly` which only passes if `errors.Is(actual, expected)` returns true. `MatchError`, by contrast, will fallback to string comparison.
## 1.38.3
### Fixes
make string formatitng more consistent for users who use format.Object directly
## 1.38.2
- roll back to go 1.23.0 [c404969]

View File

@@ -262,7 +262,7 @@ func Object(object any, indentation uint) string {
if err, ok := object.(error); ok && !isNilValue(value) { // isNilValue check needed here to avoid nil deref due to boxed nil
commonRepresentation += "\n" + IndentString(err.Error(), indentation) + "\n" + indent
}
return fmt.Sprintf("%s<%s>: %s%s", indent, formatType(value), commonRepresentation, formatValue(value, indentation, true))
return fmt.Sprintf("%s<%s>: %s%s", indent, formatType(value), commonRepresentation, formatValue(value, indentation))
}
/*
@@ -306,7 +306,7 @@ func formatType(v reflect.Value) string {
}
}
func formatValue(value reflect.Value, indentation uint, isTopLevel bool) string {
func formatValue(value reflect.Value, indentation uint) string {
if indentation > MaxDepth {
return "..."
}
@@ -367,11 +367,11 @@ func formatValue(value reflect.Value, indentation uint, isTopLevel bool) string
case reflect.Func:
return fmt.Sprintf("0x%x", value.Pointer())
case reflect.Ptr:
return formatValue(value.Elem(), indentation, isTopLevel)
return formatValue(value.Elem(), indentation)
case reflect.Slice:
return truncateLongStrings(formatSlice(value, indentation))
case reflect.String:
return truncateLongStrings(formatString(value.String(), indentation, isTopLevel))
return truncateLongStrings(formatString(value.String(), indentation))
case reflect.Array:
return truncateLongStrings(formatSlice(value, indentation))
case reflect.Map:
@@ -392,8 +392,8 @@ func formatValue(value reflect.Value, indentation uint, isTopLevel bool) string
}
}
func formatString(object any, indentation uint, isTopLevel bool) string {
if isTopLevel {
func formatString(object any, indentation uint) string {
if indentation == 1 {
s := fmt.Sprintf("%s", object)
components := strings.Split(s, "\n")
result := ""
@@ -416,14 +416,14 @@ func formatString(object any, indentation uint, isTopLevel bool) string {
func formatSlice(v reflect.Value, indentation uint) string {
if v.Kind() == reflect.Slice && v.Type().Elem().Kind() == reflect.Uint8 && isPrintableString(string(v.Bytes())) {
return formatString(v.Bytes(), indentation, false)
return formatString(v.Bytes(), indentation)
}
l := v.Len()
result := make([]string, l)
longest := 0
for i := range l {
result[i] = formatValue(v.Index(i), indentation+1, false)
for i := 0; i < l; i++ {
result[i] = formatValue(v.Index(i), indentation+1)
if len(result[i]) > longest {
longest = len(result[i])
}
@@ -443,7 +443,7 @@ func formatMap(v reflect.Value, indentation uint) string {
longest := 0
for i, key := range v.MapKeys() {
value := v.MapIndex(key)
result[i] = fmt.Sprintf("%s: %s", formatValue(key, indentation+1, false), formatValue(value, indentation+1, false))
result[i] = fmt.Sprintf("%s: %s", formatValue(key, indentation+1), formatValue(value, indentation+1))
if len(result[i]) > longest {
longest = len(result[i])
}
@@ -462,10 +462,10 @@ func formatStruct(v reflect.Value, indentation uint) string {
l := v.NumField()
result := []string{}
longest := 0
for i := range l {
for i := 0; i < l; i++ {
structField := t.Field(i)
fieldEntry := v.Field(i)
representation := fmt.Sprintf("%s: %s", structField.Name, formatValue(fieldEntry, indentation+1, false))
representation := fmt.Sprintf("%s: %s", structField.Name, formatValue(fieldEntry, indentation+1))
result = append(result, representation)
if len(representation) > longest {
longest = len(representation)
@@ -479,7 +479,7 @@ func formatStruct(v reflect.Value, indentation uint) string {
}
func formatInterface(v reflect.Value, indentation uint) string {
return fmt.Sprintf("<%s>%s", formatType(v.Elem()), formatValue(v.Elem(), indentation, false))
return fmt.Sprintf("<%s>%s", formatType(v.Elem()), formatValue(v.Elem(), indentation))
}
func isNilValue(a reflect.Value) bool {

View File

@@ -22,7 +22,7 @@ import (
"github.com/onsi/gomega/types"
)
const GOMEGA_VERSION = "1.39.0"
const GOMEGA_VERSION = "1.38.2"
const nilGomegaPanic = `You are trying to make an assertion, but haven't registered Gomega's fail handler.
If you're using Ginkgo then you probably forgot to put your assertion in an It().

View File

@@ -146,24 +146,6 @@ func MatchError(expected any, functionErrorDescription ...any) types.GomegaMatch
}
}
// MatchErrorStrictly succeeds iff actual is a non-nil error that matches the passed in
// expected error according to errors.Is(actual, expected).
//
// This behavior differs from MatchError where
//
// Expect(errors.New("some error")).To(MatchError(errors.New("some error")))
//
// succeeds, but errors.Is would return false so:
//
// Expect(errors.New("some error")).To(MatchErrorStrictly(errors.New("some error")))
//
// fails.
func MatchErrorStrictly(expected error) types.GomegaMatcher {
return &matchers.MatchErrorStrictlyMatcher{
Expected: expected,
}
}
// BeClosed succeeds if actual is a closed channel.
// It is an error to pass a non-channel to BeClosed, it is also an error to pass nil
//
@@ -533,8 +515,8 @@ func HaveExistingField(field string) types.GomegaMatcher {
// and even interface values.
//
// actual := 42
// Expect(actual).To(HaveValue(Equal(42)))
// Expect(&actual).To(HaveValue(Equal(42)))
// Expect(actual).To(HaveValue(42))
// Expect(&actual).To(HaveValue(42))
func HaveValue(matcher types.GomegaMatcher) types.GomegaMatcher {
return &matchers.HaveValueMatcher{
Matcher: matcher,

View File

@@ -39,7 +39,7 @@ func (matcher *HaveKeyMatcher) Match(actual any) (success bool, err error) {
}
keys := reflect.ValueOf(actual).MapKeys()
for i := range keys {
for i := 0; i < len(keys); i++ {
success, err := keyMatcher.Match(keys[i].Interface())
if err != nil {
return false, fmt.Errorf("HaveKey's key matcher failed with:\n%s%s", format.Indent, err.Error())

View File

@@ -52,7 +52,7 @@ func (matcher *HaveKeyWithValueMatcher) Match(actual any) (success bool, err err
}
keys := reflect.ValueOf(actual).MapKeys()
for i := range keys {
for i := 0; i < len(keys); i++ {
success, err := keyMatcher.Match(keys[i].Interface())
if err != nil {
return false, fmt.Errorf("HaveKeyWithValue's key matcher failed with:\n%s%s", format.Indent, err.Error())

View File

@@ -1,39 +0,0 @@
package matchers
import (
"errors"
"fmt"
"github.com/onsi/gomega/format"
)
type MatchErrorStrictlyMatcher struct {
Expected error
}
func (matcher *MatchErrorStrictlyMatcher) Match(actual any) (success bool, err error) {
if isNil(matcher.Expected) {
return false, fmt.Errorf("Expected error is nil, use \"ToNot(HaveOccurred())\" to explicitly check for nil errors")
}
if isNil(actual) {
return false, fmt.Errorf("Expected an error, got nil")
}
if !isError(actual) {
return false, fmt.Errorf("Expected an error. Got:\n%s", format.Object(actual, 1))
}
actualErr := actual.(error)
return errors.Is(actualErr, matcher.Expected), nil
}
func (matcher *MatchErrorStrictlyMatcher) FailureMessage(actual any) (message string) {
return format.Message(actual, "to match error", matcher.Expected)
}
func (matcher *MatchErrorStrictlyMatcher) NegatedFailureMessage(actual any) (message string) {
return format.Message(actual, "not to match error", matcher.Expected)
}

View File

@@ -1,9 +1,6 @@
package edge
import (
. "github.com/onsi/gomega/matchers/support/goraph/node"
"slices"
)
import . "github.com/onsi/gomega/matchers/support/goraph/node"
type Edge struct {
Node1 int
@@ -23,7 +20,13 @@ func (ec EdgeSet) Free(node Node) bool {
}
func (ec EdgeSet) Contains(edge Edge) bool {
return slices.Contains(ec, edge)
for _, e := range ec {
if e == edge {
return true
}
}
return false
}
func (ec EdgeSet) FindByNodes(node1, node2 Node) (Edge, bool) {

View File

@@ -57,10 +57,3 @@ linters:
- common-false-positives
- legacy
- std-error-handling
settings:
govet:
# Disable buildtag check to allow dual build tag syntax (both //go:build and // +build).
# This is necessary for Go 1.15 compatibility since //go:build was introduced in Go 1.17.
# This can be removed once Cobra requires Go 1.17 or higher.
disable:
- buildtag

View File

@@ -557,7 +557,7 @@ func (c *Command) FlagErrorFunc() (f func(*Command, error) error) {
}
}
const minUsagePadding = 25
var minUsagePadding = 25
// UsagePadding return padding for the usage.
func (c *Command) UsagePadding() int {
@@ -567,7 +567,7 @@ func (c *Command) UsagePadding() int {
return c.parent.commandsMaxUseLen
}
const minCommandPathPadding = 11
var minCommandPathPadding = 11
// CommandPathPadding return padding for the command path.
func (c *Command) CommandPathPadding() int {
@@ -577,7 +577,7 @@ func (c *Command) CommandPathPadding() int {
return c.parent.commandsMaxCommandPathLen
}
const minNamePadding = 11
var minNamePadding = 11
// NamePadding returns padding for the name.
func (c *Command) NamePadding() int {
@@ -1939,7 +1939,7 @@ type tmplFunc struct {
fn func(io.Writer, interface{}) error
}
const defaultUsageTemplate = `Usage:{{if .Runnable}}
var defaultUsageTemplate = `Usage:{{if .Runnable}}
{{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
{{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
@@ -2039,7 +2039,7 @@ func defaultUsageFunc(w io.Writer, in interface{}) error {
return nil
}
const defaultHelpTemplate = `{{with (or .Long .Short)}}{{. | trimTrailingWhitespaces}}
var defaultHelpTemplate = `{{with (or .Long .Short)}}{{. | trimTrailingWhitespaces}}
{{end}}{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`
@@ -2061,7 +2061,7 @@ func defaultHelpFunc(w io.Writer, in interface{}) error {
return nil
}
const defaultVersionTemplate = `{{with .DisplayName}}{{printf "%s " .}}{{end}}{{printf "version %s" .Version}}
var defaultVersionTemplate = `{{with .DisplayName}}{{printf "%s " .}}{{end}}{{printf "version %s" .Version}}
`
// defaultVersionFunc is equivalent to executing defaultVersionTemplate. The two should be changed in sync.

View File

@@ -38,9 +38,6 @@ type chacha20poly1305 struct {
// New returns a ChaCha20-Poly1305 AEAD that uses the given 256-bit key.
func New(key []byte) (cipher.AEAD, error) {
if fips140Enforced() {
return nil, errors.New("chacha20poly1305: use of ChaCha20Poly1305 is not allowed in FIPS 140-only mode")
}
if len(key) != KeySize {
return nil, errors.New("chacha20poly1305: bad key length")
}

View File

@@ -1,9 +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.26
package chacha20poly1305
func fips140Enforced() bool { return false }

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.26
package chacha20poly1305
import "crypto/fips140"
func fips140Enforced() bool { return fips140.Enforced() }

View File

@@ -22,9 +22,6 @@ type xchacha20poly1305 struct {
// preferred when nonce uniqueness cannot be trivially ensured, or whenever
// nonces are randomly generated.
func NewX(key []byte) (cipher.AEAD, error) {
if fips140Enforced() {
return nil, errors.New("chacha20poly1305: use of ChaCha20Poly1305 is not allowed in FIPS 140-only mode")
}
if len(key) != KeySize {
return nil, errors.New("chacha20poly1305: bad key length")
}

View File

@@ -802,8 +802,8 @@ func MatchPrefixPatterns(globs, target string) bool {
for globs != "" {
// Extract next non-empty glob in comma-separated list.
var glob string
if before, after, ok := strings.Cut(globs, ","); ok {
glob, globs = before, after
if i := strings.Index(globs, ","); i >= 0 {
glob, globs = globs[:i], globs[i+1:]
} else {
glob, globs = globs, ""
}

View File

@@ -37,15 +37,6 @@ type priorityWriteSchedulerRFC9218 struct {
// incremental streams or not, when urgency is the same in a given Pop()
// call.
prioritizeIncremental bool
// priorityUpdateBuf is used to buffer the most recent PRIORITY_UPDATE we
// receive per https://www.rfc-editor.org/rfc/rfc9218.html#name-the-priority_update-frame.
priorityUpdateBuf struct {
// streamID being 0 means that the buffer is empty. This is a safe
// assumption as PRIORITY_UPDATE for stream 0 is a PROTOCOL_ERROR.
streamID uint32
priority PriorityParam
}
}
func newPriorityWriteSchedulerRFC9218() WriteScheduler {
@@ -59,10 +50,6 @@ func (ws *priorityWriteSchedulerRFC9218) OpenStream(streamID uint32, opt OpenStr
if ws.streams[streamID].location != nil {
panic(fmt.Errorf("stream %d already opened", streamID))
}
if streamID == ws.priorityUpdateBuf.streamID {
ws.priorityUpdateBuf.streamID = 0
opt.priority = ws.priorityUpdateBuf.priority
}
q := ws.queuePool.get()
ws.streams[streamID] = streamMetadata{
location: q,
@@ -108,8 +95,6 @@ func (ws *priorityWriteSchedulerRFC9218) AdjustStream(streamID uint32, priority
metadata := ws.streams[streamID]
q, u, i := metadata.location, metadata.priority.urgency, metadata.priority.incremental
if q == nil {
ws.priorityUpdateBuf.streamID = streamID
ws.priorityUpdateBuf.priority = priority
return
}

View File

@@ -64,80 +64,6 @@ func initOptions() {
func archInit() {
// From internal/cpu
const (
// eax bits
cpuid_AVXVNNI = 1 << 4
// ecx bits
cpuid_SSE3 = 1 << 0
cpuid_PCLMULQDQ = 1 << 1
cpuid_AVX512VBMI = 1 << 1
cpuid_AVX512VBMI2 = 1 << 6
cpuid_SSSE3 = 1 << 9
cpuid_AVX512GFNI = 1 << 8
cpuid_AVX512VAES = 1 << 9
cpuid_AVX512VNNI = 1 << 11
cpuid_AVX512BITALG = 1 << 12
cpuid_FMA = 1 << 12
cpuid_AVX512VPOPCNTDQ = 1 << 14
cpuid_SSE41 = 1 << 19
cpuid_SSE42 = 1 << 20
cpuid_POPCNT = 1 << 23
cpuid_AES = 1 << 25
cpuid_OSXSAVE = 1 << 27
cpuid_AVX = 1 << 28
// "Extended Feature Flag" bits returned in EBX for CPUID EAX=0x7 ECX=0x0
cpuid_BMI1 = 1 << 3
cpuid_AVX2 = 1 << 5
cpuid_BMI2 = 1 << 8
cpuid_ERMS = 1 << 9
cpuid_AVX512F = 1 << 16
cpuid_AVX512DQ = 1 << 17
cpuid_ADX = 1 << 19
cpuid_AVX512CD = 1 << 28
cpuid_SHA = 1 << 29
cpuid_AVX512BW = 1 << 30
cpuid_AVX512VL = 1 << 31
// "Extended Feature Flag" bits returned in ECX for CPUID EAX=0x7 ECX=0x0
cpuid_AVX512_VBMI = 1 << 1
cpuid_AVX512_VBMI2 = 1 << 6
cpuid_GFNI = 1 << 8
cpuid_AVX512VPCLMULQDQ = 1 << 10
cpuid_AVX512_BITALG = 1 << 12
// edx bits
cpuid_FSRM = 1 << 4
// edx bits for CPUID 0x80000001
cpuid_RDTSCP = 1 << 27
)
// Additional constants not in internal/cpu
const (
// eax=1: edx
cpuid_SSE2 = 1 << 26
// eax=1: ecx
cpuid_CX16 = 1 << 13
cpuid_RDRAND = 1 << 30
// eax=7,ecx=0: ebx
cpuid_RDSEED = 1 << 18
cpuid_AVX512IFMA = 1 << 21
cpuid_AVX512PF = 1 << 26
cpuid_AVX512ER = 1 << 27
// eax=7,ecx=0: edx
cpuid_AVX5124VNNIW = 1 << 2
cpuid_AVX5124FMAPS = 1 << 3
cpuid_AMXBF16 = 1 << 22
cpuid_AMXTile = 1 << 24
cpuid_AMXInt8 = 1 << 25
// eax=7,ecx=1: eax
cpuid_AVX512BF16 = 1 << 5
cpuid_AVXIFMA = 1 << 23
// eax=7,ecx=1: edx
cpuid_AVXVNNIInt8 = 1 << 4
)
Initialized = true
maxID, _, _, _ := cpuid(0, 0)
@@ -147,90 +73,90 @@ func archInit() {
}
_, _, ecx1, edx1 := cpuid(1, 0)
X86.HasSSE2 = isSet(edx1, cpuid_SSE2)
X86.HasSSE2 = isSet(26, edx1)
X86.HasSSE3 = isSet(ecx1, cpuid_SSE3)
X86.HasPCLMULQDQ = isSet(ecx1, cpuid_PCLMULQDQ)
X86.HasSSSE3 = isSet(ecx1, cpuid_SSSE3)
X86.HasFMA = isSet(ecx1, cpuid_FMA)
X86.HasCX16 = isSet(ecx1, cpuid_CX16)
X86.HasSSE41 = isSet(ecx1, cpuid_SSE41)
X86.HasSSE42 = isSet(ecx1, cpuid_SSE42)
X86.HasPOPCNT = isSet(ecx1, cpuid_POPCNT)
X86.HasAES = isSet(ecx1, cpuid_AES)
X86.HasOSXSAVE = isSet(ecx1, cpuid_OSXSAVE)
X86.HasRDRAND = isSet(ecx1, cpuid_RDRAND)
X86.HasSSE3 = isSet(0, ecx1)
X86.HasPCLMULQDQ = isSet(1, ecx1)
X86.HasSSSE3 = isSet(9, ecx1)
X86.HasFMA = isSet(12, ecx1)
X86.HasCX16 = isSet(13, ecx1)
X86.HasSSE41 = isSet(19, ecx1)
X86.HasSSE42 = isSet(20, ecx1)
X86.HasPOPCNT = isSet(23, ecx1)
X86.HasAES = isSet(25, ecx1)
X86.HasOSXSAVE = isSet(27, ecx1)
X86.HasRDRAND = isSet(30, ecx1)
var osSupportsAVX, osSupportsAVX512 bool
// For XGETBV, OSXSAVE bit is required and sufficient.
if X86.HasOSXSAVE {
eax, _ := xgetbv()
// Check if XMM and YMM registers have OS support.
osSupportsAVX = isSet(eax, 1<<1) && isSet(eax, 1<<2)
osSupportsAVX = isSet(1, eax) && isSet(2, eax)
if runtime.GOOS == "darwin" {
// Darwin requires special AVX512 checks, see cpu_darwin_x86.go
osSupportsAVX512 = osSupportsAVX && darwinSupportsAVX512()
} else {
// Check if OPMASK and ZMM registers have OS support.
osSupportsAVX512 = osSupportsAVX && isSet(eax, 1<<5) && isSet(eax, 1<<6) && isSet(eax, 1<<7)
osSupportsAVX512 = osSupportsAVX && isSet(5, eax) && isSet(6, eax) && isSet(7, eax)
}
}
X86.HasAVX = isSet(ecx1, cpuid_AVX) && osSupportsAVX
X86.HasAVX = isSet(28, ecx1) && osSupportsAVX
if maxID < 7 {
return
}
eax7, ebx7, ecx7, edx7 := cpuid(7, 0)
X86.HasBMI1 = isSet(ebx7, cpuid_BMI1)
X86.HasAVX2 = isSet(ebx7, cpuid_AVX2) && osSupportsAVX
X86.HasBMI2 = isSet(ebx7, cpuid_BMI2)
X86.HasERMS = isSet(ebx7, cpuid_ERMS)
X86.HasRDSEED = isSet(ebx7, cpuid_RDSEED)
X86.HasADX = isSet(ebx7, cpuid_ADX)
X86.HasBMI1 = isSet(3, ebx7)
X86.HasAVX2 = isSet(5, ebx7) && osSupportsAVX
X86.HasBMI2 = isSet(8, ebx7)
X86.HasERMS = isSet(9, ebx7)
X86.HasRDSEED = isSet(18, ebx7)
X86.HasADX = isSet(19, ebx7)
X86.HasAVX512 = isSet(ebx7, cpuid_AVX512F) && osSupportsAVX512 // Because avx-512 foundation is the core required extension
X86.HasAVX512 = isSet(16, ebx7) && osSupportsAVX512 // Because avx-512 foundation is the core required extension
if X86.HasAVX512 {
X86.HasAVX512F = true
X86.HasAVX512CD = isSet(ebx7, cpuid_AVX512CD)
X86.HasAVX512ER = isSet(ebx7, cpuid_AVX512ER)
X86.HasAVX512PF = isSet(ebx7, cpuid_AVX512PF)
X86.HasAVX512VL = isSet(ebx7, cpuid_AVX512VL)
X86.HasAVX512BW = isSet(ebx7, cpuid_AVX512BW)
X86.HasAVX512DQ = isSet(ebx7, cpuid_AVX512DQ)
X86.HasAVX512IFMA = isSet(ebx7, cpuid_AVX512IFMA)
X86.HasAVX512VBMI = isSet(ecx7, cpuid_AVX512_VBMI)
X86.HasAVX5124VNNIW = isSet(edx7, cpuid_AVX5124VNNIW)
X86.HasAVX5124FMAPS = isSet(edx7, cpuid_AVX5124FMAPS)
X86.HasAVX512VPOPCNTDQ = isSet(ecx7, cpuid_AVX512VPOPCNTDQ)
X86.HasAVX512VPCLMULQDQ = isSet(ecx7, cpuid_AVX512VPCLMULQDQ)
X86.HasAVX512VNNI = isSet(ecx7, cpuid_AVX512VNNI)
X86.HasAVX512GFNI = isSet(ecx7, cpuid_AVX512GFNI)
X86.HasAVX512VAES = isSet(ecx7, cpuid_AVX512VAES)
X86.HasAVX512VBMI2 = isSet(ecx7, cpuid_AVX512VBMI2)
X86.HasAVX512BITALG = isSet(ecx7, cpuid_AVX512BITALG)
X86.HasAVX512CD = isSet(28, ebx7)
X86.HasAVX512ER = isSet(27, ebx7)
X86.HasAVX512PF = isSet(26, ebx7)
X86.HasAVX512VL = isSet(31, ebx7)
X86.HasAVX512BW = isSet(30, ebx7)
X86.HasAVX512DQ = isSet(17, ebx7)
X86.HasAVX512IFMA = isSet(21, ebx7)
X86.HasAVX512VBMI = isSet(1, ecx7)
X86.HasAVX5124VNNIW = isSet(2, edx7)
X86.HasAVX5124FMAPS = isSet(3, edx7)
X86.HasAVX512VPOPCNTDQ = isSet(14, ecx7)
X86.HasAVX512VPCLMULQDQ = isSet(10, ecx7)
X86.HasAVX512VNNI = isSet(11, ecx7)
X86.HasAVX512GFNI = isSet(8, ecx7)
X86.HasAVX512VAES = isSet(9, ecx7)
X86.HasAVX512VBMI2 = isSet(6, ecx7)
X86.HasAVX512BITALG = isSet(12, ecx7)
}
X86.HasAMXTile = isSet(edx7, cpuid_AMXTile)
X86.HasAMXInt8 = isSet(edx7, cpuid_AMXInt8)
X86.HasAMXBF16 = isSet(edx7, cpuid_AMXBF16)
X86.HasAMXTile = isSet(24, edx7)
X86.HasAMXInt8 = isSet(25, edx7)
X86.HasAMXBF16 = isSet(22, edx7)
// These features depend on the second level of extended features.
if eax7 >= 1 {
eax71, _, _, edx71 := cpuid(7, 1)
if X86.HasAVX512 {
X86.HasAVX512BF16 = isSet(eax71, cpuid_AVX512BF16)
X86.HasAVX512BF16 = isSet(5, eax71)
}
if X86.HasAVX {
X86.HasAVXIFMA = isSet(eax71, cpuid_AVXIFMA)
X86.HasAVXVNNI = isSet(eax71, cpuid_AVXVNNI)
X86.HasAVXVNNIInt8 = isSet(edx71, cpuid_AVXVNNIInt8)
X86.HasAVXIFMA = isSet(23, eax71)
X86.HasAVXVNNI = isSet(4, eax71)
X86.HasAVXVNNIInt8 = isSet(4, edx71)
}
}
}
func isSet(hwc uint32, value uint32) bool {
return hwc&value != 0
func isSet(bitpos uint, value uint32) bool {
return value&(1<<bitpos) != 0
}

28
vendor/golang.org/x/term/terminal.go generated vendored
View File

@@ -160,9 +160,7 @@ const (
keyEnd
keyDeleteWord
keyDeleteLine
keyDelete
keyClearScreen
keyTranspose
keyPasteStart
keyPasteEnd
)
@@ -196,8 +194,6 @@ func bytesToKey(b []byte, pasteActive bool) (rune, []byte) {
return keyDeleteLine, b[1:]
case 12: // ^L
return keyClearScreen, b[1:]
case 20: // ^T
return keyTranspose, b[1:]
case 23: // ^W
return keyDeleteWord, b[1:]
case 14: // ^N
@@ -232,10 +228,6 @@ func bytesToKey(b []byte, pasteActive bool) (rune, []byte) {
}
}
if !pasteActive && len(b) >= 4 && b[0] == keyEscape && b[1] == '[' && b[2] == '3' && b[3] == '~' {
return keyDelete, b[4:]
}
if !pasteActive && len(b) >= 6 && b[0] == keyEscape && b[1] == '[' && b[2] == '1' && b[3] == ';' && b[4] == '3' {
switch b[5] {
case 'C':
@@ -598,7 +590,7 @@ func (t *Terminal) handleKey(key rune) (line string, ok bool) {
}
t.line = t.line[:t.pos]
t.moveCursorToPos(t.pos)
case keyCtrlD, keyDelete:
case keyCtrlD:
// Erase the character under the current position.
// The EOF case when the line is empty is handled in
// readLine().
@@ -608,24 +600,6 @@ func (t *Terminal) handleKey(key rune) (line string, ok bool) {
}
case keyCtrlU:
t.eraseNPreviousChars(t.pos)
case keyTranspose:
// This transposes the two characters around the cursor and advances the cursor. Best-effort.
if len(t.line) < 2 || t.pos < 1 {
return
}
swap := t.pos
if swap == len(t.line) {
swap-- // special: at end of line, swap previous two chars
}
t.line[swap-1], t.line[swap] = t.line[swap], t.line[swap-1]
if t.pos < len(t.line) {
t.pos++
}
if t.echo {
t.moveCursorToPos(swap - 1)
t.writeLine(t.line[swap-1:])
t.moveCursorToPos(t.pos)
}
case keyClearScreen:
// Erases the screen and moves the cursor to the home position.
t.queue([]rune("\x1b[2J\x1b[H"))

View File

@@ -9,7 +9,6 @@ import (
"fmt"
"go/ast"
"go/token"
"reflect"
"slices"
"strconv"
"strings"
@@ -150,7 +149,7 @@ func AddNamedImport(fset *token.FileSet, f *ast.File, name, path string) (added
if newImport.Name != nil {
newImport.Name.NamePos = pos
}
updateBasicLitPos(newImport.Path, pos)
newImport.Path.ValuePos = pos
newImport.EndPos = pos
// Clean up parens. impDecl contains at least one spec.
@@ -185,7 +184,7 @@ func AddNamedImport(fset *token.FileSet, f *ast.File, name, path string) (added
first.Lparen = first.Pos()
// Move the imports of the other import declaration to the first one.
for _, spec := range gen.Specs {
updateBasicLitPos(spec.(*ast.ImportSpec).Path, first.Pos())
spec.(*ast.ImportSpec).Path.ValuePos = first.Pos()
first.Specs = append(first.Specs, spec)
}
f.Decls = slices.Delete(f.Decls, i, i+1)
@@ -471,17 +470,3 @@ func Imports(fset *token.FileSet, f *ast.File) [][]*ast.ImportSpec {
return groups
}
// updateBasicLitPos updates lit.Pos,
// ensuring that lit.End (if set) is displaced by the same amount.
// (See https://go.dev/issue/76395.)
func updateBasicLitPos(lit *ast.BasicLit, pos token.Pos) {
len := lit.End() - lit.Pos()
lit.ValuePos = pos
// TODO(adonovan): after go1.26, simplify to:
// lit.ValueEnd = pos + len
v := reflect.ValueOf(lit).Elem().FieldByName("ValueEnd")
if v.IsValid() && v.Int() != 0 {
v.SetInt(int64(pos + len))
}
}

View File

@@ -1027,15 +1027,11 @@ func (ld *loader) refine(response *DriverResponse) ([]*Package, error) {
// Precondition: ld.Mode&(NeedSyntax|NeedTypes|NeedTypesInfo) != 0.
func (ld *loader) loadPackage(lpkg *loaderPackage) {
if lpkg.PkgPath == "unsafe" {
// To avoid surprises, fill in the blanks consistent
// with other packages. (For example, some analyzers
// assert that each needed types.Info map is non-nil
// even when there is no syntax that would cause them
// to consult the map.)
// Fill in the blanks to avoid surprises.
lpkg.Types = types.Unsafe
lpkg.Fset = ld.Fset
lpkg.Syntax = []*ast.File{}
lpkg.TypesInfo = ld.newTypesInfo()
lpkg.TypesInfo = new(types.Info)
lpkg.TypesSizes = ld.sizes
return
}
@@ -1184,7 +1180,20 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
return
}
lpkg.TypesInfo = ld.newTypesInfo()
// Populate TypesInfo only if needed, as it
// causes the type checker to work much harder.
if ld.Config.Mode&NeedTypesInfo != 0 {
lpkg.TypesInfo = &types.Info{
Types: make(map[ast.Expr]types.TypeAndValue),
Defs: make(map[*ast.Ident]types.Object),
Uses: make(map[*ast.Ident]types.Object),
Implicits: make(map[ast.Node]types.Object),
Instances: make(map[*ast.Ident]types.Instance),
Scopes: make(map[ast.Node]*types.Scope),
Selections: make(map[*ast.SelectorExpr]*types.Selection),
FileVersions: make(map[*ast.File]string),
}
}
lpkg.TypesSizes = ld.sizes
importer := importerFunc(func(path string) (*types.Package, error) {
@@ -1298,24 +1307,6 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
lpkg.IllTyped = illTyped
}
func (ld *loader) newTypesInfo() *types.Info {
// Populate TypesInfo only if needed, as it
// causes the type checker to work much harder.
if ld.Config.Mode&NeedTypesInfo == 0 {
return nil
}
return &types.Info{
Types: make(map[ast.Expr]types.TypeAndValue),
Defs: make(map[*ast.Ident]types.Object),
Uses: make(map[*ast.Ident]types.Object),
Implicits: make(map[ast.Node]types.Object),
Instances: make(map[*ast.Ident]types.Instance),
Scopes: make(map[ast.Node]*types.Scope),
Selections: make(map[*ast.SelectorExpr]*types.Selection),
FileVersions: make(map[*ast.File]string),
}
}
// An importFunc is an implementation of the single-method
// types.Importer interface based on a function value.
type importerFunc func(path string) (*types.Package, error)

View File

@@ -12,7 +12,6 @@ import (
// Callee returns the named target of a function call, if any:
// a function, method, builtin, or variable.
// It returns nil for a T(x) conversion.
//
// Functions and methods may potentially have type parameters.
//

View File

@@ -8,6 +8,7 @@ import (
"context"
"sync/atomic"
"time"
"unsafe"
"golang.org/x/tools/internal/event/label"
)
@@ -16,21 +17,23 @@ import (
// It may return a modified context and event.
type Exporter func(context.Context, Event, label.Map) context.Context
var exporter atomic.Pointer[Exporter]
var (
exporter unsafe.Pointer
)
// SetExporter sets the global exporter function that handles all events.
// The exporter is called synchronously from the event call site, so it should
// return quickly so as not to hold up user code.
func SetExporter(e Exporter) {
p := unsafe.Pointer(&e)
if e == nil {
// &e is always valid, and so p is always valid, but for the early abort
// of ProcessEvent to be efficient it needs to make the nil check on the
// pointer without having to dereference it, so we make the nil function
// also a nil pointer
exporter.Store(nil)
} else {
exporter.Store(&e)
p = nil
}
atomic.StorePointer(&exporter, p)
}
// deliver is called to deliver an event to the supplied exporter.
@@ -45,7 +48,7 @@ func deliver(ctx context.Context, exporter Exporter, ev Event) context.Context {
// Export is called to deliver an event to the global exporter if set.
func Export(ctx context.Context, ev Event) context.Context {
// get the global exporter and abort early if there is not one
exporterPtr := exporter.Load()
exporterPtr := (*Exporter)(atomic.LoadPointer(&exporter))
if exporterPtr == nil {
return ctx
}
@@ -58,7 +61,7 @@ func Export(ctx context.Context, ev Event) context.Context {
// It will fill in the time.
func ExportPair(ctx context.Context, begin, end Event) (context.Context, func()) {
// get the global exporter and abort early if there is not one
exporterPtr := exporter.Load()
exporterPtr := (*Exporter)(atomic.LoadPointer(&exporter))
if exporterPtr == nil {
return ctx, func() {}
}

View File

@@ -7,6 +7,7 @@ package label
import (
"fmt"
"io"
"reflect"
"slices"
"unsafe"
)
@@ -102,10 +103,11 @@ type stringptr unsafe.Pointer
// This method is for implementing new key types, label creation should
// normally be done with the Of method of the key.
func OfString(k Key, v string) Label {
hdr := (*reflect.StringHeader)(unsafe.Pointer(&v))
return Label{
key: k,
packed: uint64(len(v)),
untyped: stringptr(unsafe.StringData(v)),
packed: uint64(hdr.Len),
untyped: stringptr(hdr.Data),
}
}
@@ -114,7 +116,11 @@ func OfString(k Key, v string) Label {
// This method is for implementing new key types, for type safety normal
// access should be done with the From method of the key.
func (t Label) UnpackString() string {
return unsafe.String((*byte)(t.untyped.(stringptr)), int(t.packed))
var v string
hdr := (*reflect.StringHeader)(unsafe.Pointer(&v))
hdr.Data = uintptr(t.untyped.(stringptr))
hdr.Len = int(t.packed)
return v
}
// Valid returns true if the Label is a valid one (it has a key).

View File

@@ -11,7 +11,6 @@ import (
"go/ast"
"go/token"
"log"
"reflect"
"slices"
"sort"
"strconv"
@@ -66,7 +65,7 @@ func sortImports(localPrefix string, tokFile *token.File, f *ast.File) {
}
// mergeImports merges all the import declarations into the first one.
// Taken from golang.org/x/tools/go/ast/astutil.
// Taken from golang.org/x/tools/ast/astutil.
// This does not adjust line numbers properly
func mergeImports(f *ast.File) {
if len(f.Decls) <= 1 {
@@ -90,7 +89,7 @@ func mergeImports(f *ast.File) {
first.Lparen = first.Pos()
// Move the imports of the other import declaration to the first one.
for _, spec := range gen.Specs {
updateBasicLitPos(spec.(*ast.ImportSpec).Path, first.Pos())
spec.(*ast.ImportSpec).Path.ValuePos = first.Pos()
first.Specs = append(first.Specs, spec)
}
f.Decls = slices.Delete(f.Decls, i, i+1)
@@ -99,7 +98,7 @@ func mergeImports(f *ast.File) {
}
// declImports reports whether gen contains an import of path.
// Taken from golang.org/x/tools/go/ast/astutil.
// Taken from golang.org/x/tools/ast/astutil.
func declImports(gen *ast.GenDecl, path string) bool {
if gen.Tok != token.IMPORT {
return false
@@ -222,7 +221,7 @@ func sortSpecs(localPrefix string, tokFile *token.File, f *ast.File, specs []ast
if s.Name != nil {
s.Name.NamePos = pos[i].Start
}
updateBasicLitPos(s.Path, pos[i].Start)
s.Path.ValuePos = pos[i].Start
s.EndPos = pos[i].End
nextSpecPos := pos[i].End
@@ -297,17 +296,3 @@ type byCommentPos []*ast.CommentGroup
func (x byCommentPos) Len() int { return len(x) }
func (x byCommentPos) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x byCommentPos) Less(i, j int) bool { return x[i].Pos() < x[j].Pos() }
// updateBasicLitPos updates lit.Pos,
// ensuring that lit.End (if set) is displaced by the same amount.
// (See https://go.dev/issue/76395.)
func updateBasicLitPos(lit *ast.BasicLit, pos token.Pos) {
len := lit.End() - lit.Pos()
lit.ValuePos = pos
// TODO(adonovan): after go1.26, simplify to:
// lit.ValueEnd = pos + len
v := reflect.ValueOf(lit).Elem().FieldByName("ValueEnd")
if v.IsValid() && v.Int() != 0 {
v.SetInt(int64(pos + len))
}
}

View File

@@ -8,8 +8,6 @@ import (
"slices"
"strconv"
"strings"
"golang.org/x/mod/module"
)
type Candidate struct {
@@ -106,15 +104,11 @@ func (ix *Index) Lookup(pkgName, name string, prefix bool) []Candidate {
if len(flds) < 2 {
continue // should never happen
}
impPath, err := module.UnescapePath(e.ImportPath)
if err != nil {
continue
}
px := Candidate{
PkgName: pkgName,
Name: flds[0],
Dir: string(e.Dir),
ImportPath: impPath,
ImportPath: e.ImportPath,
Type: asLexType(flds[1][0]),
Deprecated: len(flds[1]) > 1 && flds[1][1] == 'D',
}

View File

@@ -12,364 +12,360 @@ type pkginfo struct {
}
var deps = [...]pkginfo{
{"archive/tar", "\x03p\x03F=\x01\n\x01$\x01\x01\x02\x05\b\x02\x01\x02\x02\f"},
{"archive/zip", "\x02\x04f\a\x03\x13\x021=\x01+\x05\x01\x0f\x03\x02\x0e\x04"},
{"bufio", "\x03p\x86\x01D\x14"},
{"bytes", "s+[\x03\fG\x02\x02"},
{"archive/tar", "\x03n\x03E<\x01\n\x01$\x01\x01\x02\x05\b\x02\x01\x02\x02\f"},
{"archive/zip", "\x02\x04d\a\x03\x12\x021<\x01+\x05\x01\x0f\x03\x02\x0e\x04"},
{"bufio", "\x03n\x84\x01D\x14"},
{"bytes", "q*Z\x03\fG\x02\x02"},
{"cmp", ""},
{"compress/bzip2", "\x02\x02\xf5\x01A"},
{"compress/flate", "\x02q\x03\x83\x01\f\x033\x01\x03"},
{"compress/gzip", "\x02\x04f\a\x03\x15nT"},
{"compress/lzw", "\x02q\x03\x83\x01"},
{"compress/zlib", "\x02\x04f\a\x03\x13\x01o"},
{"container/heap", "\xbb\x02"},
{"compress/bzip2", "\x02\x02\xf1\x01A"},
{"compress/flate", "\x02o\x03\x81\x01\f\x033\x01\x03"},
{"compress/gzip", "\x02\x04d\a\x03\x14mT"},
{"compress/lzw", "\x02o\x03\x81\x01"},
{"compress/zlib", "\x02\x04d\a\x03\x12\x01n"},
{"container/heap", "\xb7\x02"},
{"container/list", ""},
{"container/ring", ""},
{"context", "s\\p\x01\r"},
{"crypto", "\x89\x01pC"},
{"crypto/aes", "\x10\n\t\x99\x02"},
{"crypto/cipher", "\x03 \x01\x01 \x12\x1c,Z"},
{"crypto/des", "\x10\x15 .,\x9d\x01\x03"},
{"crypto/dsa", "E\x04*\x86\x01\r"},
{"crypto/ecdh", "\x03\v\f\x10\x04\x17\x04\x0e\x1c\x86\x01"},
{"crypto/ecdsa", "\x0e\x05\x03\x04\x01\x10\b\v\x06\x01\x04\r\x01\x1c\x86\x01\r\x05K\x01"},
{"crypto/ed25519", "\x0e\x1e\x12\a\v\a\x1c\x86\x01C"},
{"crypto/elliptic", "3@\x86\x01\r9"},
{"context", "q[o\x01\r"},
{"crypto", "\x86\x01oC"},
{"crypto/aes", "\x10\n\t\x95\x02"},
{"crypto/cipher", "\x03 \x01\x01\x1f\x11\x1c+Y"},
{"crypto/des", "\x10\x15\x1f-+\x9c\x01\x03"},
{"crypto/dsa", "D\x04)\x84\x01\r"},
{"crypto/ecdh", "\x03\v\f\x10\x04\x16\x04\r\x1c\x84\x01"},
{"crypto/ecdsa", "\x0e\x05\x03\x04\x01\x10\a\v\x06\x01\x04\f\x01\x1c\x84\x01\r\x05K\x01"},
{"crypto/ed25519", "\x0e\x1e\x11\a\n\a\x1c\x84\x01C"},
{"crypto/elliptic", "2?\x84\x01\r9"},
{"crypto/fips140", "\"\x05"},
{"crypto/hkdf", "/\x15\x01.\x16"},
{"crypto/hmac", "\x1a\x16\x14\x01\x122"},
{"crypto/internal/boring", "\x0e\x02\rl"},
{"crypto/internal/boring/bbig", "\x1a\xec\x01M"},
{"crypto/internal/boring/bcache", "\xc0\x02\x13"},
{"crypto/hkdf", "/\x14\x01-\x15"},
{"crypto/hmac", "\x1a\x16\x13\x01\x111"},
{"crypto/internal/boring", "\x0e\x02\ri"},
{"crypto/internal/boring/bbig", "\x1a\xe8\x01M"},
{"crypto/internal/boring/bcache", "\xbc\x02\x13"},
{"crypto/internal/boring/sig", ""},
{"crypto/internal/constanttime", ""},
{"crypto/internal/cryptotest", "\x03\r\n\b&\x0f\x19\x06\x13\x12 \x04\x06\t\x19\x01\x11\x11\x1b\x01\a\x05\b\x03\x05\v"},
{"crypto/internal/entropy", "J"},
{"crypto/internal/entropy/v1.0.0", "C0\x95\x018\x13"},
{"crypto/internal/fips140", "B1\xbf\x01\v\x16"},
{"crypto/internal/fips140/aes", "\x03\x1f\x03\x02\x14\x05\x01\x01\x06+\x95\x014"},
{"crypto/internal/fips140/aes/gcm", "\"\x01\x02\x02\x02\x12\x05\x01\a+\x92\x01"},
{"crypto/internal/fips140/alias", "\xd3\x02"},
{"crypto/internal/fips140/bigmod", "'\x19\x01\a+\x95\x01"},
{"crypto/internal/fips140/check", "\"\x0e\a\t\x02\xb7\x01Z"},
{"crypto/internal/fips140/check/checktest", "'\x8b\x02!"},
{"crypto/internal/fips140/drbg", "\x03\x1e\x01\x01\x04\x14\x05\t\x01)\x86\x01\x0f7\x01"},
{"crypto/internal/fips140/ecdh", "\x03\x1f\x05\x02\n\r3\x86\x01\x0f7"},
{"crypto/internal/fips140/ecdsa", "\x03\x1f\x04\x01\x02\a\x03\x06:\x16pF"},
{"crypto/internal/fips140/ed25519", "\x03\x1f\x05\x02\x04\f:\xc9\x01\x03"},
{"crypto/internal/fips140/edwards25519", "\x1e\t\a\x123\x95\x017"},
{"crypto/internal/fips140/edwards25519/field", "'\x14\x053\x95\x01"},
{"crypto/internal/fips140/hkdf", "\x03\x1f\x05\t\a<\x16"},
{"crypto/internal/fips140/hmac", "\x03\x1f\x15\x01\x01:\x16"},
{"crypto/internal/fips140/mldsa", "\x03\x1b\x04\x05\x02\x0e\x01\x03\x053\x95\x017"},
{"crypto/internal/fips140/mlkem", "\x03\x1f\x05\x02\x0f\x03\x053\xcc\x01"},
{"crypto/internal/fips140/nistec", "\x1e\t\r\f3\x95\x01*\r\x14"},
{"crypto/internal/fips140/nistec/fiat", "'\x148\x95\x01"},
{"crypto/internal/fips140/pbkdf2", "\x03\x1f\x05\t\a<\x16"},
{"crypto/internal/fips140/rsa", "\x03\x1b\x04\x04\x01\x02\x0e\x01\x01\x028\x16pF"},
{"crypto/internal/fips140/sha256", "\x03\x1f\x1e\x01\a+\x16\x7f"},
{"crypto/internal/fips140/sha3", "\x03\x1f\x19\x05\x012\x95\x01K"},
{"crypto/internal/fips140/sha512", "\x03\x1f\x1e\x01\a+\x16\x7f"},
{"crypto/internal/fips140/ssh", "'b"},
{"crypto/internal/fips140/subtle", "\x1e\a\x1b\xc8\x01"},
{"crypto/internal/fips140/tls12", "\x03\x1f\x05\t\a\x02:\x16"},
{"crypto/internal/fips140/tls13", "\x03\x1f\x05\b\b\t3\x16"},
{"crypto/internal/fips140cache", "\xb2\x02\r&"},
{"crypto/internal/cryptotest", "\x03\r\n\b%\x0e\x19\x06\x12\x12 \x04\x06\t\x18\x01\x11\x11\x1b\x01\a\x05\b\x03\x05\v"},
{"crypto/internal/entropy", "I"},
{"crypto/internal/entropy/v1.0.0", "B/\x93\x018\x13"},
{"crypto/internal/fips140", "A0\xbd\x01\v\x16"},
{"crypto/internal/fips140/aes", "\x03\x1f\x03\x02\x13\x05\x01\x01\x06*\x93\x014"},
{"crypto/internal/fips140/aes/gcm", "\"\x01\x02\x02\x02\x11\x05\x01\a*\x90\x01"},
{"crypto/internal/fips140/alias", "\xcf\x02"},
{"crypto/internal/fips140/bigmod", "'\x18\x01\a*\x93\x01"},
{"crypto/internal/fips140/check", "\"\x0e\x06\t\x02\xb4\x01Z"},
{"crypto/internal/fips140/check/checktest", "'\x87\x02!"},
{"crypto/internal/fips140/drbg", "\x03\x1e\x01\x01\x04\x13\x05\t\x01(\x84\x01\x0f7\x01"},
{"crypto/internal/fips140/ecdh", "\x03\x1f\x05\x02\t\r2\x84\x01\x0f7"},
{"crypto/internal/fips140/ecdsa", "\x03\x1f\x04\x01\x02\a\x02\x069\x15oF"},
{"crypto/internal/fips140/ed25519", "\x03\x1f\x05\x02\x04\v9\xc7\x01\x03"},
{"crypto/internal/fips140/edwards25519", "\x1e\t\a\x112\x93\x017"},
{"crypto/internal/fips140/edwards25519/field", "'\x13\x052\x93\x01"},
{"crypto/internal/fips140/hkdf", "\x03\x1f\x05\t\x06;\x15"},
{"crypto/internal/fips140/hmac", "\x03\x1f\x14\x01\x019\x15"},
{"crypto/internal/fips140/mlkem", "\x03\x1f\x05\x02\x0e\x03\x052\xca\x01"},
{"crypto/internal/fips140/nistec", "\x1e\t\f\f2\x93\x01*\r\x14"},
{"crypto/internal/fips140/nistec/fiat", "'\x137\x93\x01"},
{"crypto/internal/fips140/pbkdf2", "\x03\x1f\x05\t\x06;\x15"},
{"crypto/internal/fips140/rsa", "\x03\x1b\x04\x04\x01\x02\r\x01\x01\x027\x15oF"},
{"crypto/internal/fips140/sha256", "\x03\x1f\x1d\x01\a*\x15~"},
{"crypto/internal/fips140/sha3", "\x03\x1f\x18\x05\x011\x93\x01K"},
{"crypto/internal/fips140/sha512", "\x03\x1f\x1d\x01\a*\x15~"},
{"crypto/internal/fips140/ssh", "'_"},
{"crypto/internal/fips140/subtle", "\x1e\a\x1a\xc5\x01"},
{"crypto/internal/fips140/tls12", "\x03\x1f\x05\t\x06\x029\x15"},
{"crypto/internal/fips140/tls13", "\x03\x1f\x05\b\a\t2\x15"},
{"crypto/internal/fips140cache", "\xae\x02\r&"},
{"crypto/internal/fips140deps", ""},
{"crypto/internal/fips140deps/byteorder", "\x9f\x01"},
{"crypto/internal/fips140deps/cpu", "\xb4\x01\a"},
{"crypto/internal/fips140deps/godebug", "\xbc\x01"},
{"crypto/internal/fips140deps/time", "\xcd\x02"},
{"crypto/internal/fips140hash", "8\x1d4\xca\x01"},
{"crypto/internal/fips140only", ")\x0e\x01\x01P3="},
{"crypto/internal/fips140deps/byteorder", "\x9c\x01"},
{"crypto/internal/fips140deps/cpu", "\xb1\x01\a"},
{"crypto/internal/fips140deps/godebug", "\xb9\x01"},
{"crypto/internal/fips140deps/time", "\xc9\x02"},
{"crypto/internal/fips140hash", "7\x1c3\xc9\x01"},
{"crypto/internal/fips140only", ")\r\x01\x01N3<"},
{"crypto/internal/fips140test", ""},
{"crypto/internal/hpke", "\x03\v\x01\x01\x03\x055\x03\x04\x01\x01\x16\a\x03\x13\xcc\x01"},
{"crypto/internal/impl", "\xbd\x02"},
{"crypto/internal/randutil", "\xf9\x01\x12"},
{"crypto/internal/sysrand", "sq! \r\r\x01\x01\f\x06"},
{"crypto/internal/sysrand/internal/seccomp", "s"},
{"crypto/md5", "\x0e7.\x16\x16i"},
{"crypto/mlkem", "\x0e$"},
{"crypto/mlkem/mlkemtest", "2\x1b&"},
{"crypto/pbkdf2", "5\x0f\x01.\x16"},
{"crypto/rand", "\x1a\b\a\x1c\x04\x01)\x86\x01\rM"},
{"crypto/rc4", "% .\xc9\x01"},
{"crypto/rsa", "\x0e\f\x01\v\x10\x0e\x01\x04\a\a\x1c\x03\x133=\f\x01"},
{"crypto/sha1", "\x0e\f+\x03+\x16\x16\x15T"},
{"crypto/sha256", "\x0e\f\x1dR"},
{"crypto/sha3", "\x0e*Q\xca\x01"},
{"crypto/sha512", "\x0e\f\x1fP"},
{"crypto/subtle", "\x1e\x1d\x9f\x01X"},
{"crypto/tls", "\x03\b\x02\x01\x01\x01\x01\x02\x01\x01\x01\x02\x01\x01\t\x01\x0e\n\x01\n\x05\x04\x01\x01\x01\x01\x02\x01\x02\x01\x17\x02\x03\x13\x16\x15\b=\x16\x16\r\b\x01\x01\x01\x02\x01\r\x06\x02\x01\x0f"},
{"crypto/tls/internal/fips140tls", "\x17\xa9\x02"},
{"crypto/x509", "\x03\v\x01\x01\x01\x01\x01\x01\x01\x016\x06\x01\x01\x02\x05\x0e\x06\x02\x02\x03F\x03:\x01\x02\b\x01\x01\x02\a\x10\x05\x01\x06\a\b\x02\x01\x02\x0e\x02\x01\x01\x02\x03\x01"},
{"crypto/x509/pkix", "i\x06\a\x90\x01G"},
{"database/sql", "\x03\nP\x16\x03\x83\x01\v\a\"\x05\b\x02\x03\x01\r\x02\x02\x02"},
{"database/sql/driver", "\rf\x03\xb7\x01\x0f\x11"},
{"debug/buildinfo", "\x03]\x02\x01\x01\b\a\x03g\x1a\x02\x01+\x0f\x1f"},
{"debug/dwarf", "\x03i\a\x03\x83\x011\x11\x01\x01"},
{"debug/elf", "\x03\x06V\r\a\x03g\x1b\x01\f \x17\x01\x16"},
{"debug/gosym", "\x03i\n\xc5\x01\x01\x01\x02"},
{"debug/macho", "\x03\x06V\r\ng\x1c,\x17\x01"},
{"debug/pe", "\x03\x06V\r\a\x03g\x1c,\x17\x01\x16"},
{"debug/plan9obj", "l\a\x03g\x1c,"},
{"embed", "s+B\x19\x01S"},
{"crypto/internal/hpke", "\x0e\x01\x01\x03\x056#+hM"},
{"crypto/internal/impl", "\xb9\x02"},
{"crypto/internal/randutil", "\xf5\x01\x12"},
{"crypto/internal/sysrand", "qo! \r\r\x01\x01\f\x06"},
{"crypto/internal/sysrand/internal/seccomp", "q"},
{"crypto/md5", "\x0e6-\x15\x16h"},
{"crypto/mlkem", "1"},
{"crypto/pbkdf2", "4\x0f\x01-\x15"},
{"crypto/rand", "\x1a\b\a\x1b\x04\x01(\x84\x01\rM"},
{"crypto/rc4", "%\x1f-\xc7\x01"},
{"crypto/rsa", "\x0e\f\x01\v\x0f\x0e\x01\x04\x06\a\x1c\x03\x123<\f\x01"},
{"crypto/sha1", "\x0e\f*\x03*\x15\x16\x15S"},
{"crypto/sha256", "\x0e\f\x1cP"},
{"crypto/sha3", "\x0e)O\xc9\x01"},
{"crypto/sha512", "\x0e\f\x1eN"},
{"crypto/subtle", "\x1e\x1c\x9c\x01X"},
{"crypto/tls", "\x03\b\x02\x01\x01\x01\x01\x02\x01\x01\x01\x02\x01\x01\t\x01\r\n\x01\n\x05\x03\x01\x01\x01\x01\x02\x01\x02\x01\x17\x02\x03\x12\x16\x15\b<\x16\x16\r\b\x01\x01\x01\x02\x01\r\x06\x02\x01\x0f"},
{"crypto/tls/internal/fips140tls", "\x17\xa5\x02"},
{"crypto/x509", "\x03\v\x01\x01\x01\x01\x01\x01\x01\x015\x05\x01\x01\x02\x05\x0e\x06\x02\x02\x03E\x039\x01\x02\b\x01\x01\x02\a\x10\x05\x01\x06\x02\x05\b\x02\x01\x02\x0e\x02\x01\x01\x02\x03\x01"},
{"crypto/x509/pkix", "g\x06\a\x8e\x01G"},
{"database/sql", "\x03\nN\x16\x03\x81\x01\v\a\"\x05\b\x02\x03\x01\r\x02\x02\x02"},
{"database/sql/driver", "\rd\x03\xb5\x01\x0f\x11"},
{"debug/buildinfo", "\x03[\x02\x01\x01\b\a\x03e\x1a\x02\x01+\x0f\x1f"},
{"debug/dwarf", "\x03g\a\x03\x81\x011\x11\x01\x01"},
{"debug/elf", "\x03\x06T\r\a\x03e\x1b\x01\f \x17\x01\x16"},
{"debug/gosym", "\x03g\n\xc3\x01\x01\x01\x02"},
{"debug/macho", "\x03\x06T\r\ne\x1c,\x17\x01"},
{"debug/pe", "\x03\x06T\r\a\x03e\x1c,\x17\x01\x16"},
{"debug/plan9obj", "j\a\x03e\x1c,"},
{"embed", "q*A\x19\x01S"},
{"embed/internal/embedtest", ""},
{"encoding", ""},
{"encoding/ascii85", "\xf9\x01C"},
{"encoding/asn1", "\x03p\x03g(\x01'\r\x02\x01\x10\x03\x01"},
{"encoding/base32", "\xf9\x01A\x02"},
{"encoding/base64", "\x9f\x01ZA\x02"},
{"encoding/binary", "s\x86\x01\f(\r\x05"},
{"encoding/csv", "\x02\x01p\x03\x83\x01D\x12\x02"},
{"encoding/gob", "\x02e\x05\a\x03g\x1c\v\x01\x03\x1d\b\x12\x01\x0f\x02"},
{"encoding/hex", "s\x03\x83\x01A\x03"},
{"encoding/json", "\x03\x01c\x04\b\x03\x83\x01\f(\r\x02\x01\x02\x10\x01\x01\x02"},
{"encoding/pem", "\x03h\b\x86\x01A\x03"},
{"encoding/xml", "\x02\x01d\f\x03\x83\x014\x05\n\x01\x02\x10\x02"},
{"errors", "\xcf\x01\x84\x01"},
{"expvar", "pLA\b\v\x15\r\b\x02\x03\x01\x11"},
{"flag", "g\f\x03\x83\x01,\b\x05\b\x02\x01\x10"},
{"fmt", "sF'\x19\f \b\r\x02\x03\x12"},
{"go/ast", "\x03\x01r\x0f\x01s\x03)\b\r\x02\x01\x12\x02"},
{"go/build", "\x02\x01p\x03\x01\x02\x02\b\x02\x01\x17\x1f\x04\x02\b\x1c\x13\x01+\x01\x04\x01\a\b\x02\x01\x12\x02\x02"},
{"go/build/constraint", "s\xc9\x01\x01\x12\x02"},
{"go/constant", "v\x10\x7f\x01\x024\x01\x02\x12"},
{"go/doc", "\x04r\x01\x05\n=61\x10\x02\x01\x12\x02"},
{"go/doc/comment", "\x03s\xc4\x01\x01\x01\x01\x12\x02"},
{"go/format", "\x03s\x01\f\x01\x02sD"},
{"go/importer", "x\a\x01\x02\x04\x01r9"},
{"go/internal/gccgoimporter", "\x02\x01]\x13\x03\x04\f\x01p\x02,\x01\x05\x11\x01\f\b"},
{"go/internal/gcimporter", "\x02t\x10\x010\x05\r0,\x15\x03\x02"},
{"go/internal/scannerhooks", "\x86\x01"},
{"go/internal/srcimporter", "v\x01\x01\v\x03\x01r,\x01\x05\x12\x02\x14"},
{"go/parser", "\x03p\x03\x01\x02\b\x04\x01s\x01+\x06\x12"},
{"go/printer", "v\x01\x02\x03\ns\f \x15\x02\x01\x02\v\x05\x02"},
{"go/scanner", "\x03s\v\x05s2\x10\x01\x13\x02"},
{"go/token", "\x04r\x86\x01>\x02\x03\x01\x0f\x02"},
{"go/types", "\x03\x01\x06i\x03\x01\x03\t\x03\x024\x063\x04\x03\t \x06\a\b\x01\x01\x01\x02\x01\x0f\x02\x02"},
{"go/version", "\xc1\x01|"},
{"hash", "\xf9\x01"},
{"hash/adler32", "s\x16\x16"},
{"hash/crc32", "s\x16\x16\x15\x8b\x01\x01\x13"},
{"hash/crc64", "s\x16\x16\xa0\x01"},
{"hash/fnv", "s\x16\x16i"},
{"hash/maphash", "\x89\x01\x11<}"},
{"html", "\xbd\x02\x02\x12"},
{"html/template", "\x03m\x06\x19-=\x01\n!\x05\x01\x02\x03\f\x01\x02\f\x01\x03\x02"},
{"image", "\x02q\x1fg\x0f4\x03\x01"},
{"encoding/ascii85", "\xf5\x01C"},
{"encoding/asn1", "\x03n\x03e(\x01'\r\x02\x01\x10\x03\x01"},
{"encoding/base32", "\xf5\x01A\x02"},
{"encoding/base64", "\x9c\x01YA\x02"},
{"encoding/binary", "q\x84\x01\f(\r\x05"},
{"encoding/csv", "\x02\x01n\x03\x81\x01D\x12\x02"},
{"encoding/gob", "\x02c\x05\a\x03e\x1c\v\x01\x03\x1d\b\x12\x01\x0f\x02"},
{"encoding/hex", "q\x03\x81\x01A\x03"},
{"encoding/json", "\x03\x01a\x04\b\x03\x81\x01\f(\r\x02\x01\x02\x10\x01\x01\x02"},
{"encoding/pem", "\x03f\b\x84\x01A\x03"},
{"encoding/xml", "\x02\x01b\f\x03\x81\x014\x05\n\x01\x02\x10\x02"},
{"errors", "\xcc\x01\x83\x01"},
{"expvar", "nK@\b\v\x15\r\b\x02\x03\x01\x11"},
{"flag", "e\f\x03\x81\x01,\b\x05\b\x02\x01\x10"},
{"fmt", "qE&\x19\f \b\r\x02\x03\x12"},
{"go/ast", "\x03\x01p\x0e\x01r\x03)\b\r\x02\x01\x12\x02"},
{"go/build", "\x02\x01n\x03\x01\x02\x02\a\x02\x01\x17\x1f\x04\x02\b\x1b\x13\x01+\x01\x04\x01\a\b\x02\x01\x12\x02\x02"},
{"go/build/constraint", "q\xc7\x01\x01\x12\x02"},
{"go/constant", "t\x0f~\x01\x024\x01\x02\x12"},
{"go/doc", "\x04p\x01\x05\t=51\x10\x02\x01\x12\x02"},
{"go/doc/comment", "\x03q\xc2\x01\x01\x01\x01\x12\x02"},
{"go/format", "\x03q\x01\v\x01\x02rD"},
{"go/importer", "v\a\x01\x01\x04\x01q9"},
{"go/internal/gccgoimporter", "\x02\x01[\x13\x03\x04\v\x01o\x02,\x01\x05\x11\x01\f\b"},
{"go/internal/gcimporter", "\x02r\x0f\x010\x05\r/,\x15\x03\x02"},
{"go/internal/srcimporter", "t\x01\x01\n\x03\x01q,\x01\x05\x12\x02\x14"},
{"go/parser", "\x03n\x03\x01\x02\v\x01r\x01+\x06\x12"},
{"go/printer", "t\x01\x02\x03\tr\f \x15\x02\x01\x02\v\x05\x02"},
{"go/scanner", "\x03q\x0fr2\x10\x01\x13\x02"},
{"go/token", "\x04p\x84\x01>\x02\x03\x01\x0f\x02"},
{"go/types", "\x03\x01\x06g\x03\x01\x03\b\x03\x024\x062\x04\x03\t \x06\a\b\x01\x01\x01\x02\x01\x0f\x02\x02"},
{"go/version", "\xbe\x01{"},
{"hash", "\xf5\x01"},
{"hash/adler32", "q\x15\x16"},
{"hash/crc32", "q\x15\x16\x15\x8a\x01\x01\x13"},
{"hash/crc64", "q\x15\x16\x9f\x01"},
{"hash/fnv", "q\x15\x16h"},
{"hash/maphash", "\x86\x01\x11<|"},
{"html", "\xb9\x02\x02\x12"},
{"html/template", "\x03k\x06\x18-<\x01\n!\x05\x01\x02\x03\f\x01\x02\f\x01\x03\x02"},
{"image", "\x02o\x1ef\x0f4\x03\x01"},
{"image/color", ""},
{"image/color/palette", "\x92\x01"},
{"image/draw", "\x91\x01\x01\x04"},
{"image/gif", "\x02\x01\x05k\x03\x1b\x01\x01\x01\vZ\x0f"},
{"image/internal/imageutil", "\x91\x01"},
{"image/jpeg", "\x02q\x1e\x01\x04c"},
{"image/png", "\x02\ac\n\x13\x02\x06\x01gC"},
{"index/suffixarray", "\x03i\a\x86\x01\f+\n\x01"},
{"internal/abi", "\xbb\x01\x98\x01"},
{"internal/asan", "\xd3\x02"},
{"internal/bisect", "\xb2\x02\r\x01"},
{"internal/buildcfg", "vHg\x06\x02\x05\n\x01"},
{"internal/bytealg", "\xb4\x01\x9f\x01"},
{"image/color/palette", "\x8f\x01"},
{"image/draw", "\x8e\x01\x01\x04"},
{"image/gif", "\x02\x01\x05i\x03\x1a\x01\x01\x01\vY"},
{"image/internal/imageutil", "\x8e\x01"},
{"image/jpeg", "\x02o\x1d\x01\x04b"},
{"image/png", "\x02\aa\n\x12\x02\x06\x01fC"},
{"index/suffixarray", "\x03g\a\x84\x01\f+\n\x01"},
{"internal/abi", "\xb8\x01\x97\x01"},
{"internal/asan", "\xcf\x02"},
{"internal/bisect", "\xae\x02\r\x01"},
{"internal/buildcfg", "tGf\x06\x02\x05\n\x01"},
{"internal/bytealg", "\xb1\x01\x9e\x01"},
{"internal/byteorder", ""},
{"internal/cfg", ""},
{"internal/cgrouptest", "v[T\x06\x0f\x02\x01\x04\x01"},
{"internal/chacha8rand", "\x9f\x01\x15\a\x98\x01"},
{"internal/cgrouptest", "tZS\x06\x0f\x02\x01\x04\x01"},
{"internal/chacha8rand", "\x9c\x01\x15\a\x97\x01"},
{"internal/copyright", ""},
{"internal/coverage", ""},
{"internal/coverage/calloc", ""},
{"internal/coverage/cfile", "p\x06\x17\x17\x01\x02\x01\x01\x01\x01\x01\x01\x01\"\x02',\x06\a\n\x01\x03\r\x06"},
{"internal/coverage/cformat", "\x04r.\x04Q\v6\x01\x02\r"},
{"internal/coverage/cmerge", "v.a"},
{"internal/coverage/decodecounter", "l\n.\v\x02H,\x17\x17"},
{"internal/coverage/decodemeta", "\x02j\n\x17\x17\v\x02H,"},
{"internal/coverage/encodecounter", "\x02j\n.\f\x01\x02F\v!\x15"},
{"internal/coverage/encodemeta", "\x02\x01i\n\x13\x04\x17\r\x02F,."},
{"internal/coverage/pods", "\x04r.\x81\x01\x06\x05\n\x02\x01"},
{"internal/coverage/rtcov", "\xd3\x02"},
{"internal/coverage/slicereader", "l\n\x83\x01Z"},
{"internal/coverage/slicewriter", "v\x83\x01"},
{"internal/coverage/stringtab", "v9\x04F"},
{"internal/coverage/cfile", "n\x06\x16\x17\x01\x02\x01\x01\x01\x01\x01\x01\x01\"\x02&,\x06\a\n\x01\x03\r\x06"},
{"internal/coverage/cformat", "\x04p-\x04P\v6\x01\x02\r"},
{"internal/coverage/cmerge", "t-`"},
{"internal/coverage/decodecounter", "j\n-\v\x02G,\x17\x17"},
{"internal/coverage/decodemeta", "\x02h\n\x16\x17\v\x02G,"},
{"internal/coverage/encodecounter", "\x02h\n-\f\x01\x02E\v!\x15"},
{"internal/coverage/encodemeta", "\x02\x01g\n\x12\x04\x17\r\x02E,."},
{"internal/coverage/pods", "\x04p-\x80\x01\x06\x05\n\x02\x01"},
{"internal/coverage/rtcov", "\xcf\x02"},
{"internal/coverage/slicereader", "j\n\x81\x01Z"},
{"internal/coverage/slicewriter", "t\x81\x01"},
{"internal/coverage/stringtab", "t8\x04E"},
{"internal/coverage/test", ""},
{"internal/coverage/uleb128", ""},
{"internal/cpu", "\xd3\x02"},
{"internal/dag", "\x04r\xc4\x01\x03"},
{"internal/diff", "\x03s\xc5\x01\x02"},
{"internal/exportdata", "\x02\x01p\x03\x02e\x1c,\x01\x05\x11\x01\x02"},
{"internal/filepathlite", "s+B\x1a@"},
{"internal/fmtsort", "\x04\xa9\x02\r"},
{"internal/fuzz", "\x03\nG\x18\x04\x03\x03\x01\f\x036=\f\x03\x1d\x01\x05\x02\x05\n\x01\x02\x01\x01\f\x04\x02"},
{"internal/cpu", "\xcf\x02"},
{"internal/dag", "\x04p\xc2\x01\x03"},
{"internal/diff", "\x03q\xc3\x01\x02"},
{"internal/exportdata", "\x02\x01n\x03\x02c\x1c,\x01\x05\x11\x01\x02"},
{"internal/filepathlite", "q*A\x1a@"},
{"internal/fmtsort", "\x04\xa5\x02\r"},
{"internal/fuzz", "\x03\nE\x18\x04\x03\x03\x01\v\x036<\f\x03\x1d\x01\x05\x02\x05\n\x01\x02\x01\x01\f\x04\x02"},
{"internal/goarch", ""},
{"internal/godebug", "\x9c\x01!\x82\x01\x01\x13"},
{"internal/godebug", "\x99\x01!\x81\x01\x01\x13"},
{"internal/godebugs", ""},
{"internal/goexperiment", ""},
{"internal/goos", ""},
{"internal/goroot", "\xa5\x02\x01\x05\x12\x02"},
{"internal/goroot", "\xa1\x02\x01\x05\x12\x02"},
{"internal/gover", "\x04"},
{"internal/goversion", ""},
{"internal/lazyregexp", "\xa5\x02\v\r\x02"},
{"internal/lazytemplate", "\xf9\x01,\x18\x02\f"},
{"internal/msan", "\xd3\x02"},
{"internal/lazyregexp", "\xa1\x02\v\r\x02"},
{"internal/lazytemplate", "\xf5\x01,\x18\x02\f"},
{"internal/msan", "\xcf\x02"},
{"internal/nettrace", ""},
{"internal/obscuretestdata", "k\x8e\x01,"},
{"internal/oserror", "s"},
{"internal/pkgbits", "\x03Q\x18\a\x03\x04\fs\r\x1f\r\n\x01"},
{"internal/obscuretestdata", "i\x8c\x01,"},
{"internal/oserror", "q"},
{"internal/pkgbits", "\x03O\x18\a\x03\x04\vr\r\x1f\r\n\x01"},
{"internal/platform", ""},
{"internal/poll", "sl\x05\x159\r\x01\x01\f\x06"},
{"internal/profile", "\x03\x04l\x03\x83\x017\n\x01\x01\x01\x10"},
{"internal/poll", "qj\x05\x159\r\x01\x01\f\x06"},
{"internal/profile", "\x03\x04j\x03\x81\x017\n\x01\x01\x01\x10"},
{"internal/profilerecord", ""},
{"internal/race", "\x9a\x01\xb9\x01"},
{"internal/reflectlite", "\x9a\x01!;<!"},
{"internal/runtime/atomic", "\xbb\x01\x98\x01"},
{"internal/runtime/cgroup", "\x9e\x01=\x04t"},
{"internal/runtime/exithook", "\xd0\x01\x83\x01"},
{"internal/runtime/gc", "\xbb\x01"},
{"internal/runtime/gc/internal/gen", "\nb\n\x18k\x04\v\x1d\b\x10\x02"},
{"internal/runtime/gc/scan", "\xb4\x01\a\x18\ay"},
{"internal/runtime/maps", "\x9a\x01\x01 \n\t\t\x03y"},
{"internal/runtime/math", "\xbb\x01"},
{"internal/runtime/pprof/label", ""},
{"internal/race", "\x97\x01\xb8\x01"},
{"internal/reflectlite", "\x97\x01!:<!"},
{"internal/runtime/atomic", "\xb8\x01\x97\x01"},
{"internal/runtime/cgroup", "\x9b\x01<\x04t"},
{"internal/runtime/exithook", "\xcd\x01\x82\x01"},
{"internal/runtime/gc", "\xb8\x01"},
{"internal/runtime/gc/internal/gen", "\n`\n\x17j\x04\v\x1d\b\x10\x02"},
{"internal/runtime/gc/scan", "\xb1\x01\a\x18\x06y"},
{"internal/runtime/maps", "\x97\x01\x01 \n\t\t\x02y"},
{"internal/runtime/math", "\xb8\x01"},
{"internal/runtime/startlinetest", ""},
{"internal/runtime/sys", "\xbb\x01\x04"},
{"internal/runtime/syscall/linux", "\xbb\x01\x98\x01"},
{"internal/runtime/sys", "\xb8\x01\x04"},
{"internal/runtime/syscall/linux", "\xb8\x01\x97\x01"},
{"internal/runtime/wasitest", ""},
{"internal/saferio", "\xf9\x01Z"},
{"internal/singleflight", "\xbf\x02"},
{"internal/strconv", "\x88\x02K"},
{"internal/stringslite", "\x9e\x01\xb5\x01"},
{"internal/sync", "\x9a\x01!\x13r\x13"},
{"internal/synctest", "\x9a\x01\xb9\x01"},
{"internal/syscall/execenv", "\xc1\x02"},
{"internal/syscall/unix", "\xb2\x02\x0e\x01\x12"},
{"internal/sysinfo", "\x02\x01\xb1\x01E,\x18\x02"},
{"internal/saferio", "\xf5\x01Z"},
{"internal/singleflight", "\xbb\x02"},
{"internal/strconv", "\x84\x02K"},
{"internal/stringslite", "\x9b\x01\xb4\x01"},
{"internal/sync", "\x97\x01!\x13q\x13"},
{"internal/synctest", "\x97\x01\xb8\x01"},
{"internal/syscall/execenv", "\xbd\x02"},
{"internal/syscall/unix", "\xae\x02\x0e\x01\x12"},
{"internal/sysinfo", "\x02\x01\xae\x01D,\x18\x02"},
{"internal/syslist", ""},
{"internal/testenv", "\x03\nf\x02\x01*\x1b\x0f0+\x01\x05\a\n\x01\x02\x02\x01\v"},
{"internal/testhash", "\x03\x86\x01p\x118\v"},
{"internal/testlog", "\xbf\x02\x01\x13"},
{"internal/testpty", "s\x03\xaf\x01"},
{"internal/trace", "\x02\x01\x01\x06b\a\x03w\x03\x03\x06\x03\t5\x01\x01\x01\x10\x06"},
{"internal/trace/internal/testgen", "\x03i\nu\x03\x02\x03\x011\v\r\x10"},
{"internal/trace/internal/tracev1", "\x03\x01h\a\x03}\x06\f5\x01"},
{"internal/trace/raw", "\x02j\nz\x03\x06C\x01\x12"},
{"internal/trace/testtrace", "\x02\x01p\x03q\x04\x03\x05\x01\x05,\v\x02\b\x02\x01\x05"},
{"internal/testenv", "\x03\nd\x02\x01)\x1b\x0f/+\x01\x05\a\n\x01\x02\x02\x01\v"},
{"internal/testhash", "\x03\x83\x01o\x118\v"},
{"internal/testlog", "\xbb\x02\x01\x13"},
{"internal/testpty", "q\x03\xad\x01"},
{"internal/trace", "\x02\x01\x01\x06`\a\x03u\x03\x03\x06\x03\t5\x01\x01\x01\x10\x06"},
{"internal/trace/internal/testgen", "\x03g\ns\x03\x02\x03\x011\v\r\x10"},
{"internal/trace/internal/tracev1", "\x03\x01f\a\x03{\x06\f5\x01"},
{"internal/trace/raw", "\x02h\nx\x03\x06C\x01\x12"},
{"internal/trace/testtrace", "\x02\x01n\x03o\x04\x03\x05\x01\x05,\v\x02\b\x02\x01\x05"},
{"internal/trace/tracev2", ""},
{"internal/trace/traceviewer", "\x02c\v\x06\x1a<\x1f\a\a\x04\b\v\x15\x01\x05\a\n\x01\x02\x0e"},
{"internal/trace/traceviewer", "\x02a\v\x06\x19<\x1e\a\a\x04\b\v\x15\x01\x05\a\n\x01\x02\x0e"},
{"internal/trace/traceviewer/format", ""},
{"internal/trace/version", "vz\t"},
{"internal/txtar", "\x03s\xaf\x01\x18"},
{"internal/types/errors", "\xbc\x02"},
{"internal/unsafeheader", "\xd3\x02"},
{"internal/xcoff", "_\r\a\x03g\x1c,\x17\x01"},
{"internal/zstd", "l\a\x03\x83\x01\x0f"},
{"io", "s\xcc\x01"},
{"io/fs", "s+*11\x10\x13\x04"},
{"io/ioutil", "\xf9\x01\x01+\x15\x03"},
{"iter", "\xce\x01d!"},
{"log", "v\x83\x01\x05'\r\r\x01\r"},
{"internal/trace/version", "tx\t"},
{"internal/txtar", "\x03q\xad\x01\x18"},
{"internal/types/errors", "\xb8\x02"},
{"internal/unsafeheader", "\xcf\x02"},
{"internal/xcoff", "]\r\a\x03e\x1c,\x17\x01"},
{"internal/zstd", "j\a\x03\x81\x01\x0f"},
{"io", "q\xca\x01"},
{"io/fs", "q**01\x10\x13\x04"},
{"io/ioutil", "\xf5\x01\x01+\x15\x03"},
{"iter", "\xcb\x01c!"},
{"log", "t\x81\x01\x05'\r\r\x01\r"},
{"log/internal", ""},
{"log/slog", "\x03\nZ\t\x03\x03\x83\x01\x04\x01\x02\x02\x03(\x05\b\x02\x01\x02\x01\r\x02\x02\x02"},
{"log/slog", "\x03\nX\t\x03\x03\x81\x01\x04\x01\x02\x02\x03(\x05\b\x02\x01\x02\x01\r\x02\x02\x02"},
{"log/slog/internal", ""},
{"log/slog/internal/benchmarks", "\rf\x03\x83\x01\x06\x03:\x11"},
{"log/slog/internal/buffer", "\xbf\x02"},
{"log/syslog", "s\x03\x87\x01\x12\x16\x18\x02\x0e"},
{"maps", "\xfc\x01W"},
{"math", "\xb4\x01TK"},
{"math/big", "\x03p\x03)\x15E\f\x03\x020\x02\x01\x02\x14"},
{"math/big/internal/asmgen", "\x03\x01r\x92\x012\x03"},
{"math/bits", "\xd3\x02"},
{"math/cmplx", "\x85\x02\x03"},
{"math/rand", "\xbc\x01I:\x01\x13"},
{"math/rand/v2", "s,\x03c\x03K"},
{"mime", "\x02\x01h\b\x03\x83\x01\v!\x15\x03\x02\x10\x02"},
{"mime/multipart", "\x02\x01M#\x03F=\v\x01\a\x02\x15\x02\x06\x0f\x02\x01\x16"},
{"mime/quotedprintable", "\x02\x01s\x83\x01"},
{"net", "\x04\tf+\x1e\n\x05\x13\x01\x01\x04\x15\x01%\x06\r\b\x05\x01\x01\f\x06\a"},
{"net/http", "\x02\x01\x03\x01\x04\x02C\b\x13\x01\a\x03F=\x01\x03\a\x01\x03\x02\x02\x01\x02\x06\x02\x01\x01\n\x01\x01\x05\x01\x02\x05\b\x01\x01\x01\x02\x01\r\x02\x02\x02\b\x01\x01\x01"},
{"net/http/cgi", "\x02V\x1b\x03\x83\x01\x04\a\v\x01\x13\x01\x01\x01\x04\x01\x05\x02\b\x02\x01\x10\x0e"},
{"net/http/cookiejar", "\x04o\x03\x99\x01\x01\b\a\x05\x16\x03\x02\x0e\x04"},
{"net/http/fcgi", "\x02\x01\n_\a\x03\x83\x01\x16\x01\x01\x14\x18\x02\x0e"},
{"net/http/httptest", "\x02\x01\nK\x02\x1b\x01\x83\x01\x04\x12\x01\n\t\x02\x17\x01\x02\x0e\x0e"},
{"net/http/httptrace", "\rKnI\x14\n "},
{"net/http/httputil", "\x02\x01\nf\x03\x83\x01\x04\x0f\x03\x01\x05\x02\x01\v\x01\x19\x02\x01\r\x0e"},
{"net/http/internal", "\x02\x01p\x03\x83\x01"},
{"net/http/internal/ascii", "\xbd\x02\x12"},
{"net/http/internal/httpcommon", "\rf\x03\x9f\x01\x0e\x01\x17\x01\x01\x02\x1c\x02"},
{"net/http/internal/testcert", "\xbd\x02"},
{"net/http/pprof", "\x02\x01\ni\x19-\x02\x0e-\x04\x13\x14\x01\r\x04\x03\x01\x02\x01\x10"},
{"log/slog/internal/benchmarks", "\rd\x03\x81\x01\x06\x03:\x11"},
{"log/slog/internal/buffer", "\xbb\x02"},
{"log/syslog", "q\x03\x85\x01\x12\x16\x18\x02\x0e"},
{"maps", "\xf8\x01W"},
{"math", "\xb1\x01SK"},
{"math/big", "\x03n\x03(\x15D\f\x03\x020\x02\x01\x02\x14"},
{"math/big/internal/asmgen", "\x03\x01p\x90\x012\x03"},
{"math/bits", "\xcf\x02"},
{"math/cmplx", "\x81\x02\x03"},
{"math/rand", "\xb9\x01H:\x01\x13"},
{"math/rand/v2", "q+\x03b\x03K"},
{"mime", "\x02\x01f\b\x03\x81\x01\v!\x15\x03\x02\x10\x02"},
{"mime/multipart", "\x02\x01K#\x03E<\v\x01\a\x02\x15\x02\x06\x0f\x02\x01\x16"},
{"mime/quotedprintable", "\x02\x01q\x81\x01"},
{"net", "\x04\td*\x1e\n\x05\x12\x01\x01\x04\x15\x01%\x06\r\b\x05\x01\x01\f\x06\a"},
{"net/http", "\x02\x01\x03\x01\x04\x02A\b\x13\x01\a\x03E<\x01\x03\a\x01\x03\x02\x02\x01\x02\x06\x02\x01\x01\n\x01\x01\x05\x01\x02\x05\b\x01\x01\x01\x02\x01\r\x02\x02\x02\b\x01\x01\x01"},
{"net/http/cgi", "\x02T\x1b\x03\x81\x01\x04\a\v\x01\x13\x01\x01\x01\x04\x01\x05\x02\b\x02\x01\x10\x0e"},
{"net/http/cookiejar", "\x04m\x03\x97\x01\x01\b\f\x16\x03\x02\x0e\x04"},
{"net/http/fcgi", "\x02\x01\n]\a\x03\x81\x01\x16\x01\x01\x14\x18\x02\x0e"},
{"net/http/httptest", "\x02\x01\nI\x02\x1b\x01\x81\x01\x04\x12\x01\n\t\x02\x17\x01\x02\x0e\x0e"},
{"net/http/httptrace", "\rImH\x14\n "},
{"net/http/httputil", "\x02\x01\nd\x03\x81\x01\x04\x0f\x03\x01\x05\x02\x01\v\x01\x19\x02\x0e\x0e"},
{"net/http/internal", "\x02\x01n\x03\x81\x01"},
{"net/http/internal/ascii", "\xb9\x02\x12"},
{"net/http/internal/httpcommon", "\rd\x03\x9d\x01\x0e\x01\x17\x01\x01\x02\x1c\x02"},
{"net/http/internal/testcert", "\xb9\x02"},
{"net/http/pprof", "\x02\x01\ng\x18-\x02\x0e,\x04\x13\x14\x01\r\x04\x03\x01\x02\x01\x10"},
{"net/internal/cgotest", ""},
{"net/internal/socktest", "v\xc9\x01\x02"},
{"net/mail", "\x02q\x03\x83\x01\x04\x0f\x03\x14\x1a\x02\x0e\x04"},
{"net/netip", "\x04o+\x01f\x034\x16"},
{"net/rpc", "\x02l\x05\x03\x10\ni\x04\x12\x01\x1d\r\x03\x02"},
{"net/rpc/jsonrpc", "p\x03\x03\x83\x01\x16\x11\x1f"},
{"net/smtp", "\x193\f\x13\b\x03\x83\x01\x16\x14\x1a"},
{"net/textproto", "\x02\x01p\x03\x83\x01\f\n-\x01\x02\x14"},
{"net/url", "s\x03Fc\v\x10\x02\x01\x16"},
{"os", "s+\x01\x19\x03\x10\x14\x01\x03\x01\x05\x10\x018\b\x05\x01\x01\f\x06"},
{"os/exec", "\x03\nfI'\x01\x15\x01+\x06\a\n\x01\x04\f"},
{"os/exec/internal/fdtest", "\xc1\x02"},
{"os/signal", "\r\x98\x02\x15\x05\x02"},
{"os/user", "\x02\x01p\x03\x83\x01,\r\n\x01\x02"},
{"path", "s+\xb3\x01"},
{"path/filepath", "s+\x1aB+\r\b\x03\x04\x10"},
{"plugin", "s"},
{"reflect", "s'\x04\x1d\x13\b\x04\x05\x17\x06\t-\n\x03\x10\x02\x02"},
{"net/internal/socktest", "t\xc7\x01\x02"},
{"net/mail", "\x02o\x03\x81\x01\x04\x0f\x03\x14\x1a\x02\x0e\x04"},
{"net/netip", "\x04m*\x01e\x034\x16"},
{"net/rpc", "\x02j\x05\x03\x0f\nh\x04\x12\x01\x1d\r\x03\x02"},
{"net/rpc/jsonrpc", "n\x03\x03\x81\x01\x16\x11\x1f"},
{"net/smtp", "\x192\v\x13\b\x03\x81\x01\x16\x14\x1a"},
{"net/textproto", "\x02\x01n\x03\x81\x01\f\n-\x01\x02\x14"},
{"net/url", "q\x03\xa7\x01\v\x10\x02\x01\x16"},
{"os", "q*\x01\x19\x03\x10\x13\x01\x03\x01\x05\x10\x018\b\x05\x01\x01\f\x06"},
{"os/exec", "\x03\ndH&\x01\x15\x01+\x06\a\n\x01\x04\f"},
{"os/exec/internal/fdtest", "\xbd\x02"},
{"os/signal", "\r\x94\x02\x15\x05\x02"},
{"os/user", "\x02\x01n\x03\x81\x01,\r\n\x01\x02"},
{"path", "q*\xb2\x01"},
{"path/filepath", "q*\x1aA+\r\b\x03\x04\x10"},
{"plugin", "q"},
{"reflect", "q&\x04\x1d\x13\b\x03\x05\x17\x06\t-\n\x03\x10\x02\x02"},
{"reflect/internal/example1", ""},
{"reflect/internal/example2", ""},
{"regexp", "\x03\xf6\x018\t\x02\x01\x02\x10\x02"},
{"regexp/syntax", "\xba\x02\x01\x01\x01\x02\x10\x02"},
{"runtime", "\x9a\x01\x04\x01\x03\f\x06\a\x02\x01\x01\x0e\x03\x01\x01\x01\x02\x01\x01\x01\x02\x01\x04\x01\x10\x18K"},
{"runtime/coverage", "\xa6\x01S"},
{"runtime/debug", "vUZ\r\b\x02\x01\x10\x06"},
{"runtime/metrics", "\xbd\x01H-!"},
{"runtime/pprof", "\x02\x01\x01\x03\x06_\a\x03$$\x0f\v!\f \r\b\x01\x01\x01\x02\x02\t\x03\x06"},
{"runtime/race", "\xb8\x02"},
{"regexp", "\x03\xf2\x018\t\x02\x01\x02\x10\x02"},
{"regexp/syntax", "\xb6\x02\x01\x01\x01\x02\x10\x02"},
{"runtime", "\x97\x01\x04\x01\x03\f\x06\a\x02\x01\x01\x0e\x03\x01\x01\x01\x02\x01\x01\x02\x01\x04\x01\x10c"},
{"runtime/coverage", "\xa3\x01R"},
{"runtime/debug", "tTY\r\b\x02\x01\x10\x06"},
{"runtime/metrics", "\xba\x01G-!"},
{"runtime/pprof", "\x02\x01\x01\x03\x06]\a\x03#$\x0f+\f \r\b\x01\x01\x01\x02\x02\t\x03\x06"},
{"runtime/race", "\xb4\x02"},
{"runtime/race/internal/amd64v1", ""},
{"runtime/trace", "\rf\x03z\t9\b\x05\x01\r\x06"},
{"slices", "\x04\xf8\x01\fK"},
{"sort", "\xcf\x0192"},
{"strconv", "s+A\x01q"},
{"strings", "s'\x04B\x19\x03\f7\x10\x02\x02"},
{"runtime/trace", "\rd\x03x\t9\b\x05\x01\r\x06"},
{"slices", "\x04\xf4\x01\fK"},
{"sort", "\xcc\x0182"},
{"strconv", "q*@\x01q"},
{"strings", "q&\x04A\x19\x03\f7\x10\x02\x02"},
{"structs", ""},
{"sync", "\xce\x01\x13\x01P\x0e\x13"},
{"sync/atomic", "\xd3\x02"},
{"syscall", "s(\x03\x01\x1c\n\x03\x06\r\x04S\b\x05\x01\x13"},
{"testing", "\x03\nf\x02\x01X\x17\x14\f\x05\x1b\x06\x02\x05\x02\x05\x01\x02\x01\x02\x01\r\x02\x04"},
{"testing/fstest", "s\x03\x83\x01\x01\n&\x10\x03\b\b"},
{"testing/internal/testdeps", "\x02\v\xad\x01/\x10,\x03\x05\x03\x06\a\x02\x0e"},
{"testing/iotest", "\x03p\x03\x83\x01\x04"},
{"testing/quick", "u\x01\x8f\x01\x05#\x10\x10"},
{"testing/slogtest", "\rf\x03\x89\x01.\x05\x10\v"},
{"testing/synctest", "\xe2\x01`\x11"},
{"text/scanner", "\x03s\x83\x01,*\x02"},
{"text/tabwriter", "v\x83\x01X"},
{"text/template", "s\x03C@\x01\n \x01\x05\x01\x02\x05\v\x02\r\x03\x02"},
{"text/template/parse", "\x03s\xbc\x01\n\x01\x12\x02"},
{"time", "s+\x1e$(*\r\x02\x12"},
{"time/tzdata", "s\xce\x01\x12"},
{"sync", "\xcb\x01\x12\x01P\x0e\x13"},
{"sync/atomic", "\xcf\x02"},
{"syscall", "q'\x03\x01\x1c\n\x03\x06\f\x04S\b\x05\x01\x13"},
{"testing", "\x03\nd\x02\x01W\x16\x14\f\x05\x1b\x06\x02\x05\x02\x05\x01\x02\x01\x02\x01\r\x02\x04"},
{"testing/fstest", "q\x03\x81\x01\x01\n&\x10\x03\b\b"},
{"testing/internal/testdeps", "\x02\v\xaa\x01.\x10,\x03\x05\x03\x06\a\x02\x0e"},
{"testing/iotest", "\x03n\x03\x81\x01\x04"},
{"testing/quick", "s\x01\x8d\x01\x05#\x10\x10"},
{"testing/slogtest", "\rd\x03\x87\x01.\x05\x10\v"},
{"testing/synctest", "\xde\x01`\x11"},
{"text/scanner", "\x03q\x81\x01,*\x02"},
{"text/tabwriter", "t\x81\x01X"},
{"text/template", "q\x03B?\x01\n \x01\x05\x01\x02\x05\v\x02\r\x03\x02"},
{"text/template/parse", "\x03q\xba\x01\n\x01\x12\x02"},
{"time", "q*\x1e#(*\r\x02\x12"},
{"time/tzdata", "q\xcc\x01\x12"},
{"unicode", ""},
{"unicode/utf16", ""},
{"unicode/utf8", ""},
{"unique", "\x9a\x01!%\x01Q\r\x01\x13\x12"},
{"unique", "\x97\x01!$\x01Q\r\x01\x13\x12"},
{"unsafe", ""},
{"vendor/golang.org/x/crypto/chacha20", "\x10\\\a\x95\x01*&"},
{"vendor/golang.org/x/crypto/chacha20poly1305", "\x10\\\a\xe1\x01\x04\x01\a"},
{"vendor/golang.org/x/crypto/cryptobyte", "i\n\x03\x90\x01' \n"},
{"vendor/golang.org/x/crypto/chacha20", "\x10Z\a\x93\x01*&"},
{"vendor/golang.org/x/crypto/chacha20poly1305", "\x10Z\a\xdf\x01\x04\x01\a"},
{"vendor/golang.org/x/crypto/cryptobyte", "g\n\x03\x8e\x01' \n"},
{"vendor/golang.org/x/crypto/cryptobyte/asn1", ""},
{"vendor/golang.org/x/crypto/internal/alias", "\xd3\x02"},
{"vendor/golang.org/x/crypto/internal/poly1305", "W\x15\x9c\x01"},
{"vendor/golang.org/x/net/dns/dnsmessage", "s\xc7\x01"},
{"vendor/golang.org/x/net/http/httpguts", "\x8f\x02\x14\x1a\x14\r"},
{"vendor/golang.org/x/net/http/httpproxy", "s\x03\x99\x01\x10\x05\x01\x18\x14\r"},
{"vendor/golang.org/x/net/http2/hpack", "\x03p\x03\x83\x01F"},
{"vendor/golang.org/x/net/idna", "v\x8f\x018\x14\x10\x02\x01"},
{"vendor/golang.org/x/net/nettest", "\x03i\a\x03\x83\x01\x11\x05\x16\x01\f\n\x01\x02\x02\x01\v"},
{"vendor/golang.org/x/sys/cpu", "\xa5\x02\r\n\x01\x16"},
{"vendor/golang.org/x/text/secure/bidirule", "s\xde\x01\x11\x01"},
{"vendor/golang.org/x/text/transform", "\x03p\x86\x01X"},
{"vendor/golang.org/x/text/unicode/bidi", "\x03\bk\x87\x01>\x16"},
{"vendor/golang.org/x/text/unicode/norm", "l\n\x83\x01F\x12\x11"},
{"weak", "\x9a\x01\x98\x01!"},
{"vendor/golang.org/x/crypto/internal/alias", "\xcf\x02"},
{"vendor/golang.org/x/crypto/internal/poly1305", "U\x15\x9a\x01"},
{"vendor/golang.org/x/net/dns/dnsmessage", "q"},
{"vendor/golang.org/x/net/http/httpguts", "\x8b\x02\x14\x1a\x14\r"},
{"vendor/golang.org/x/net/http/httpproxy", "q\x03\x97\x01\x10\x05\x01\x18\x14\r"},
{"vendor/golang.org/x/net/http2/hpack", "\x03n\x03\x81\x01F"},
{"vendor/golang.org/x/net/idna", "t\x8d\x018\x14\x10\x02\x01"},
{"vendor/golang.org/x/net/nettest", "\x03g\a\x03\x81\x01\x11\x05\x16\x01\f\n\x01\x02\x02\x01\v"},
{"vendor/golang.org/x/sys/cpu", "\xa1\x02\r\n\x01\x16"},
{"vendor/golang.org/x/text/secure/bidirule", "q\xdc\x01\x11\x01"},
{"vendor/golang.org/x/text/transform", "\x03n\x84\x01X"},
{"vendor/golang.org/x/text/unicode/bidi", "\x03\bi\x85\x01>\x16"},
{"vendor/golang.org/x/text/unicode/norm", "j\n\x81\x01F\x12\x11"},
{"weak", "\x97\x01\x97\x01!"},
}
// bootstrap is the list of bootstrap packages extracted from cmd/dist.
@@ -389,7 +385,6 @@ var bootstrap = map[string]bool{
"cmd/compile/internal/arm64": true,
"cmd/compile/internal/base": true,
"cmd/compile/internal/bitvec": true,
"cmd/compile/internal/bloop": true,
"cmd/compile/internal/compare": true,
"cmd/compile/internal/coverage": true,
"cmd/compile/internal/deadlocals": true,
@@ -418,7 +413,6 @@ var bootstrap = map[string]bool{
"cmd/compile/internal/riscv64": true,
"cmd/compile/internal/rttype": true,
"cmd/compile/internal/s390x": true,
"cmd/compile/internal/slice": true,
"cmd/compile/internal/ssa": true,
"cmd/compile/internal/ssagen": true,
"cmd/compile/internal/staticdata": true,

View File

File diff suppressed because it is too large Load Diff

View File

@@ -39,7 +39,7 @@ const (
Var // "EOF"
Const // "Pi"
Field // "Point.X"
Method // "(*Buffer).Grow" or "(Reader).Read"
Method // "(*Buffer).Grow"
)
func (kind Kind) String() string {

View File

@@ -8,7 +8,7 @@ import (
"fmt"
"go/ast"
"go/types"
_ "unsafe" // for go:linkname hack
_ "unsafe"
)
// CallKind describes the function position of an [*ast.CallExpr].

View File

@@ -23,6 +23,7 @@ import (
"go/token"
"go/types"
"reflect"
"unsafe"
"golang.org/x/tools/go/ast/inspector"
"golang.org/x/tools/internal/aliases"
@@ -39,7 +40,8 @@ func SetUsesCgo(conf *types.Config) bool {
}
}
*(*bool)(f.Addr().UnsafePointer()) = true
addr := unsafe.Pointer(f.UnsafeAddr())
*(*bool)(addr) = true
return true
}

View File

@@ -9,7 +9,6 @@ package versions
// named constants, to avoid misspelling
const (
Go1_17 = "go1.17"
Go1_18 = "go1.18"
Go1_19 = "go1.19"
Go1_20 = "go1.20"

27
vendor/modules.txt vendored
View File

@@ -255,13 +255,13 @@ github.com/cespare/xxhash/v2
# github.com/cevaris/ordered_map v0.0.0-20190319150403-3adeae072e73
## explicit
github.com/cevaris/ordered_map
# github.com/clipperhouse/displaywidth v0.6.0
# github.com/clipperhouse/displaywidth v0.3.1
## explicit; go 1.18
github.com/clipperhouse/displaywidth
# github.com/clipperhouse/stringish v0.1.1
## explicit; go 1.18
github.com/clipperhouse/stringish
# github.com/clipperhouse/uax29/v2 v2.3.0
# github.com/clipperhouse/uax29/v2 v2.2.0
## explicit; go 1.18
github.com/clipperhouse/uax29/v2/graphemes
github.com/clipperhouse/uax29/v2/internal/iterators
@@ -1197,15 +1197,14 @@ github.com/olekukonko/cat
# github.com/olekukonko/errors v1.1.0
## explicit; go 1.21
github.com/olekukonko/errors
# github.com/olekukonko/ll v0.1.3
# github.com/olekukonko/ll v0.1.2
## explicit; go 1.21
github.com/olekukonko/ll
github.com/olekukonko/ll/lh
github.com/olekukonko/ll/lx
# github.com/olekukonko/tablewriter v1.1.2
# github.com/olekukonko/tablewriter v1.1.1
## explicit; go 1.21
github.com/olekukonko/tablewriter
github.com/olekukonko/tablewriter/pkg/twcache
github.com/olekukonko/tablewriter/pkg/twwarp
github.com/olekukonko/tablewriter/pkg/twwidth
github.com/olekukonko/tablewriter/renderer
@@ -1264,7 +1263,7 @@ github.com/onsi/ginkgo/v2/internal/reporters
github.com/onsi/ginkgo/v2/internal/testingtproxy
github.com/onsi/ginkgo/v2/reporters
github.com/onsi/ginkgo/v2/types
# github.com/onsi/gomega v1.39.0
# github.com/onsi/gomega v1.38.2
## explicit; go 1.23.0
github.com/onsi/gomega
github.com/onsi/gomega/format
@@ -2054,7 +2053,7 @@ github.com/spf13/afero/mem
## explicit; go 1.21.0
github.com/spf13/cast
github.com/spf13/cast/internal
# github.com/spf13/cobra v1.10.2
# github.com/spf13/cobra v1.10.1
## explicit; go 1.15
github.com/spf13/cobra
# github.com/spf13/pflag v1.0.10
@@ -2423,7 +2422,7 @@ go.yaml.in/yaml/v2
# go.yaml.in/yaml/v3 v3.0.4
## explicit; go 1.16
go.yaml.in/yaml/v3
# golang.org/x/crypto v0.47.0
# golang.org/x/crypto v0.46.0
## explicit; go 1.24.0
golang.org/x/crypto/argon2
golang.org/x/crypto/bcrypt
@@ -2475,12 +2474,12 @@ golang.org/x/image/vector
golang.org/x/image/vp8
golang.org/x/image/vp8l
golang.org/x/image/webp
# golang.org/x/mod v0.31.0
# golang.org/x/mod v0.30.0
## explicit; go 1.24.0
golang.org/x/mod/internal/lazyregexp
golang.org/x/mod/module
golang.org/x/mod/semver
# golang.org/x/net v0.49.0
# golang.org/x/net v0.48.0
## explicit; go 1.24.0
golang.org/x/net/bpf
golang.org/x/net/context
@@ -2513,7 +2512,7 @@ golang.org/x/oauth2/internal
golang.org/x/sync/errgroup
golang.org/x/sync/semaphore
golang.org/x/sync/singleflight
# golang.org/x/sys v0.40.0
# golang.org/x/sys v0.39.0
## explicit; go 1.24.0
golang.org/x/sys/cpu
golang.org/x/sys/execabs
@@ -2524,10 +2523,10 @@ golang.org/x/sys/windows/registry
golang.org/x/sys/windows/svc
golang.org/x/sys/windows/svc/eventlog
golang.org/x/sys/windows/svc/mgr
# golang.org/x/term v0.39.0
# golang.org/x/term v0.38.0
## explicit; go 1.24.0
golang.org/x/term
# golang.org/x/text v0.33.0
# golang.org/x/text v0.32.0
## explicit; go 1.24.0
golang.org/x/text/cases
golang.org/x/text/encoding
@@ -2555,7 +2554,7 @@ golang.org/x/text/width
# golang.org/x/time v0.14.0
## explicit; go 1.24.0
golang.org/x/time/rate
# golang.org/x/tools v0.40.0
# golang.org/x/tools v0.39.0
## explicit; go 1.24.0
golang.org/x/tools/cover
golang.org/x/tools/go/ast/astutil