update deps
This commit is contained in:
parent
4b4223b3c5
commit
255d8a446a
168 changed files with 48767 additions and 57913 deletions
34
go.mod
34
go.mod
|
|
@ -11,14 +11,14 @@ require (
|
||||||
github.com/etkecc/go-env v1.2.1
|
github.com/etkecc/go-env v1.2.1
|
||||||
github.com/etkecc/go-fswatcher v1.0.1
|
github.com/etkecc/go-fswatcher v1.0.1
|
||||||
github.com/etkecc/go-healthchecks/v2 v2.2.2
|
github.com/etkecc/go-healthchecks/v2 v2.2.2
|
||||||
github.com/etkecc/go-kit v1.7.5
|
github.com/etkecc/go-kit v1.7.6
|
||||||
github.com/etkecc/go-linkpearl v0.0.0-20251016112544-0d85fb3bccdc
|
github.com/etkecc/go-linkpearl v0.0.0-20251209104933-55b9c56572bb
|
||||||
github.com/etkecc/go-mxidwc v1.0.1
|
github.com/etkecc/go-mxidwc v1.0.1
|
||||||
github.com/etkecc/go-secgen v1.4.0
|
github.com/etkecc/go-secgen v1.4.0
|
||||||
github.com/etkecc/go-validator/v2 v2.2.6
|
github.com/etkecc/go-validator/v2 v2.2.6
|
||||||
github.com/fsnotify/fsnotify v1.9.0
|
github.com/fsnotify/fsnotify v1.9.0
|
||||||
github.com/gabriel-vasile/mimetype v1.4.11
|
github.com/gabriel-vasile/mimetype v1.4.11
|
||||||
github.com/getsentry/sentry-go v0.36.2
|
github.com/getsentry/sentry-go v0.40.0
|
||||||
github.com/jhillyerd/enmime/v2 v2.2.0
|
github.com/jhillyerd/enmime/v2 v2.2.0
|
||||||
github.com/kvannotten/mailstrip v0.0.0-20200711213611-0002f5c0467e
|
github.com/kvannotten/mailstrip v0.0.0-20200711213611-0002f5c0467e
|
||||||
github.com/lib/pq v1.10.9
|
github.com/lib/pq v1.10.9
|
||||||
|
|
@ -26,9 +26,9 @@ require (
|
||||||
github.com/raja/argon2pw v1.0.2-0.20210910183755-a391af63bd39
|
github.com/raja/argon2pw v1.0.2-0.20210910183755-a391af63bd39
|
||||||
github.com/rs/zerolog v1.34.0
|
github.com/rs/zerolog v1.34.0
|
||||||
github.com/swaggo/swag v1.16.3
|
github.com/swaggo/swag v1.16.3
|
||||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546
|
golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39
|
||||||
maunium.net/go/mautrix v0.25.2
|
maunium.net/go/mautrix v0.26.0
|
||||||
modernc.org/sqlite v1.40.0
|
modernc.org/sqlite v1.40.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
|
@ -39,6 +39,7 @@ require (
|
||||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||||
github.com/buger/jsonparser v1.1.1 // indirect
|
github.com/buger/jsonparser v1.1.1 // indirect
|
||||||
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
|
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
|
||||||
|
github.com/clipperhouse/displaywidth v0.6.1 // indirect
|
||||||
github.com/clipperhouse/stringish v0.1.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.3.0 // indirect
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
|
|
@ -62,26 +63,25 @@ require (
|
||||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect
|
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect
|
||||||
github.com/olekukonko/errors v1.1.0 // indirect
|
github.com/olekukonko/errors v1.1.0 // indirect
|
||||||
github.com/olekukonko/ll v0.1.2 // indirect
|
github.com/olekukonko/ll v0.1.3 // indirect
|
||||||
github.com/olekukonko/tablewriter v1.1.0 // indirect
|
github.com/olekukonko/tablewriter v1.1.2 // indirect
|
||||||
github.com/petermattis/goid v0.0.0-20250904145737-900bdf8bb490 // indirect
|
github.com/petermattis/goid v0.0.0-20251121121749-a11dd1a45f9a // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
|
||||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
||||||
github.com/tidwall/gjson v1.18.0 // indirect
|
github.com/tidwall/gjson v1.18.0 // indirect
|
||||||
github.com/tidwall/match v1.2.0 // indirect
|
github.com/tidwall/match v1.2.0 // indirect
|
||||||
github.com/tidwall/pretty v1.2.1 // indirect
|
github.com/tidwall/pretty v1.2.1 // indirect
|
||||||
github.com/tidwall/sjson v1.2.5 // indirect
|
github.com/tidwall/sjson v1.2.5 // indirect
|
||||||
github.com/yuin/goldmark v1.7.13 // indirect
|
github.com/yuin/goldmark v1.7.13 // indirect
|
||||||
go.mau.fi/util v0.9.2 // indirect
|
go.mau.fi/util v0.9.3 // indirect
|
||||||
golang.org/x/crypto v0.43.0 // indirect
|
golang.org/x/crypto v0.46.0 // indirect
|
||||||
golang.org/x/net v0.46.0 // indirect
|
golang.org/x/net v0.48.0 // indirect
|
||||||
golang.org/x/sys v0.37.0 // indirect
|
golang.org/x/sys v0.39.0 // indirect
|
||||||
golang.org/x/text v0.30.0 // indirect
|
golang.org/x/text v0.32.0 // indirect
|
||||||
golang.org/x/tools v0.38.0 // indirect
|
golang.org/x/tools v0.39.0 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
modernc.org/libc v1.66.10 // indirect
|
modernc.org/libc v1.67.1 // indirect
|
||||||
modernc.org/mathutil v1.7.1 // indirect
|
modernc.org/mathutil v1.7.1 // indirect
|
||||||
modernc.org/memory v1.11.0 // indirect
|
modernc.org/memory v1.11.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
||||||
90
go.sum
90
go.sum
|
|
@ -16,6 +16,8 @@ github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMU
|
||||||
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
||||||
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a h1:MISbI8sU/PSK/ztvmWKFcI7UGb5/HQT7B+i3a2myKgI=
|
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a h1:MISbI8sU/PSK/ztvmWKFcI7UGb5/HQT7B+i3a2myKgI=
|
||||||
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a/go.mod h1:2GxOXOlEPAMFPfp014mK1SWq8G8BN8o7/dfYqJrVGn8=
|
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a/go.mod h1:2GxOXOlEPAMFPfp014mK1SWq8G8BN8o7/dfYqJrVGn8=
|
||||||
|
github.com/clipperhouse/displaywidth v0.6.1 h1:/zMlAezfDzT2xy6acHBzwIfyu2ic0hgkT83UX5EY2gY=
|
||||||
|
github.com/clipperhouse/displaywidth v0.6.1/go.mod h1:R+kHuzaYWFkTm7xoMmK1lFydbci4X2CicfbGstSGg0o=
|
||||||
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
|
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/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 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4=
|
||||||
|
|
@ -41,10 +43,10 @@ github.com/etkecc/go-fswatcher v1.0.1 h1:n9hqtjzTS3ETb9hcZe1EqYA1lkkhlzZlztu3hXw
|
||||||
github.com/etkecc/go-fswatcher v1.0.1/go.mod h1:O5TODJ9z6Qb7X+snqHbB+F0Pah6G497Wdg0SFE/UYpE=
|
github.com/etkecc/go-fswatcher v1.0.1/go.mod h1:O5TODJ9z6Qb7X+snqHbB+F0Pah6G497Wdg0SFE/UYpE=
|
||||||
github.com/etkecc/go-healthchecks/v2 v2.2.2 h1:YV7e+Ga8JY3aZX4Qf6Q1Ca+DnqbT5Drjli3PufB4J1g=
|
github.com/etkecc/go-healthchecks/v2 v2.2.2 h1:YV7e+Ga8JY3aZX4Qf6Q1Ca+DnqbT5Drjli3PufB4J1g=
|
||||||
github.com/etkecc/go-healthchecks/v2 v2.2.2/go.mod h1:IowWGN4F6By6z0eh63+639OscylgtdvT9ITjfN6hnZA=
|
github.com/etkecc/go-healthchecks/v2 v2.2.2/go.mod h1:IowWGN4F6By6z0eh63+639OscylgtdvT9ITjfN6hnZA=
|
||||||
github.com/etkecc/go-kit v1.7.5 h1:DbHTbJ69Jnt2d8mjOyKA9f2ARxvi8xgnYgVMlMZd3gI=
|
github.com/etkecc/go-kit v1.7.6 h1:BruKhh6FDfeGe+pBGEBPeIJZTKEXFQxBUfExmePS3h8=
|
||||||
github.com/etkecc/go-kit v1.7.5/go.mod h1:ZFeQrvlMIV6OeZ4XJ090kkyNYRRN4+5wcg1vjnOKEGI=
|
github.com/etkecc/go-kit v1.7.6/go.mod h1:ZFeQrvlMIV6OeZ4XJ090kkyNYRRN4+5wcg1vjnOKEGI=
|
||||||
github.com/etkecc/go-linkpearl v0.0.0-20251016112544-0d85fb3bccdc h1:jUwnFg8OxXgp6JEmVyuR0sfUqV3pEbnWJqZnrroCM60=
|
github.com/etkecc/go-linkpearl v0.0.0-20251209104933-55b9c56572bb h1:jGNx9yUaxC0xfamMOzgmMY3bq5PEDOB5wXmAtZcDpRg=
|
||||||
github.com/etkecc/go-linkpearl v0.0.0-20251016112544-0d85fb3bccdc/go.mod h1:bg9M+agdjBrD0nS3uGUh7iP59mwlVZMw2ah+z3JzNoY=
|
github.com/etkecc/go-linkpearl v0.0.0-20251209104933-55b9c56572bb/go.mod h1:XLjaRo8Bfk6iiQ+//eCFTw6LViBt4rB9SUBwEggGd/U=
|
||||||
github.com/etkecc/go-mxidwc v1.0.1 h1:t4Kq3FxSlQjt1i7RpzE5q3cOWjJ0vrTzzGZRSpgh8mg=
|
github.com/etkecc/go-mxidwc v1.0.1 h1:t4Kq3FxSlQjt1i7RpzE5q3cOWjJ0vrTzzGZRSpgh8mg=
|
||||||
github.com/etkecc/go-mxidwc v1.0.1/go.mod h1:WFlntcH4mdual/gNi6X7a6rSJERNuLjdrwM3K/tucQA=
|
github.com/etkecc/go-mxidwc v1.0.1/go.mod h1:WFlntcH4mdual/gNi6X7a6rSJERNuLjdrwM3K/tucQA=
|
||||||
github.com/etkecc/go-secgen v1.4.0 h1:PapLpen3aIqG2LDu+U6KWDb4SMW2UGuvetpJjd86yUc=
|
github.com/etkecc/go-secgen v1.4.0 h1:PapLpen3aIqG2LDu+U6KWDb4SMW2UGuvetpJjd86yUc=
|
||||||
|
|
@ -59,8 +61,8 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S
|
||||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.11 h1:AQvxbp830wPhHTqc1u7nzoLT+ZFxGY7emj5DR5DYFik=
|
github.com/gabriel-vasile/mimetype v1.4.11 h1:AQvxbp830wPhHTqc1u7nzoLT+ZFxGY7emj5DR5DYFik=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.11/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
github.com/gabriel-vasile/mimetype v1.4.11/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||||
github.com/getsentry/sentry-go v0.36.2 h1:uhuxRPTrUy0dnSzTd0LrYXlBYygLkKY0hhlG5LXarzM=
|
github.com/getsentry/sentry-go v0.40.0 h1:VTJMN9zbTvqDqPwheRVLcp0qcUcM+8eFivvGocAaSbo=
|
||||||
github.com/getsentry/sentry-go v0.36.2/go.mod h1:p5Im24mJBeruET8Q4bbcMfCQ+F+Iadc4L48tB1apo2c=
|
github.com/getsentry/sentry-go v0.40.0/go.mod h1:eRXCoh3uvmjQLY6qu63BjUZnaBu5L5WhMV1RwYO8W5s=
|
||||||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||||
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||||
|
|
@ -130,12 +132,12 @@ 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/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 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM=
|
||||||
github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
|
github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
|
||||||
github.com/olekukonko/ll v0.1.2 h1:lkg/k/9mlsy0SxO5aC+WEpbdT5K83ddnNhAepz7TQc0=
|
github.com/olekukonko/ll v0.1.3 h1:sV2jrhQGq5B3W0nENUISCR6azIPf7UBUpVq0x/y70Fg=
|
||||||
github.com/olekukonko/ll v0.1.2/go.mod h1:b52bVQRRPObe+yyBl0TxNfhesL0nedD4Cht0/zx55Ew=
|
github.com/olekukonko/ll v0.1.3/go.mod h1:b52bVQRRPObe+yyBl0TxNfhesL0nedD4Cht0/zx55Ew=
|
||||||
github.com/olekukonko/tablewriter v1.1.0 h1:N0LHrshF4T39KvI96fn6GT8HEjXRXYNDrDjKFDB7RIY=
|
github.com/olekukonko/tablewriter v1.1.2 h1:L2kI1Y5tZBct/O/TyZK1zIE9GlBj/TVs+AY5tZDCDSc=
|
||||||
github.com/olekukonko/tablewriter v1.1.0/go.mod h1:5c+EBPeSqvXnLLgkm9isDdzR3wjfBkHR9Nhfp3NWrzo=
|
github.com/olekukonko/tablewriter v1.1.2/go.mod h1:z7SYPugVqGVavWoA2sGsFIoOVNmEHxUAAMrhXONtfkg=
|
||||||
github.com/petermattis/goid v0.0.0-20250904145737-900bdf8bb490 h1:QTvNkZ5ylY0PGgA+Lih+GdboMLY/G9SEGLMEGVjTVA4=
|
github.com/petermattis/goid v0.0.0-20251121121749-a11dd1a45f9a h1:VweslR2akb/ARhXfqSfRbj1vpWwYXf3eeAUyw/ndms0=
|
||||||
github.com/petermattis/goid v0.0.0-20250904145737-900bdf8bb490/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
|
github.com/petermattis/goid v0.0.0-20251121121749-a11dd1a45f9a/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
|
||||||
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
|
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
|
||||||
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
|
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
|
@ -146,8 +148,6 @@ github.com/raja/argon2pw v1.0.2-0.20210910183755-a391af63bd39 h1:2by0+lF6NfaNWhl
|
||||||
github.com/raja/argon2pw v1.0.2-0.20210910183755-a391af63bd39/go.mod h1:idX/fPqwjX31YMTF2iIpEpNApV2YbQhSFr4iIhJaqp4=
|
github.com/raja/argon2pw v1.0.2-0.20210910183755-a391af63bd39/go.mod h1:idX/fPqwjX31YMTF2iIpEpNApV2YbQhSFr4iIhJaqp4=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
|
||||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
|
||||||
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
||||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||||
|
|
@ -175,39 +175,39 @@ github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
||||||
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||||
github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA=
|
github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA=
|
||||||
github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||||
go.mau.fi/util v0.9.2 h1:+S4Z03iCsGqU2WY8X2gySFsFjaLlUHFRDVCYvVwynKM=
|
go.mau.fi/util v0.9.3 h1:aqNF8KDIN8bFpFbybSk+mEBil7IHeBwlujfyTnvP0uU=
|
||||||
go.mau.fi/util v0.9.2/go.mod h1:055elBBCJSdhRsmub7ci9hXZPgGr1U6dYg44cSgRgoU=
|
go.mau.fi/util v0.9.3/go.mod h1:krWWfBM1jWTb5f8NCa2TLqWMQuM81X7TGQjhMjBeXmQ=
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
|
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||||
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
|
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY=
|
golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 h1:DHNhtq3sNNzrvduZZIiFyXWOL9IWaDPHqTnLJp+rCBY=
|
||||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70=
|
golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0=
|
||||||
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
|
||||||
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
|
||||||
golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
|
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
|
||||||
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
|
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||||
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
|
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q=
|
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
|
||||||
golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
|
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
|
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||||
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
|
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
|
||||||
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|
@ -220,20 +220,22 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
maunium.net/go/mautrix v0.25.2 h1:CUG23zp754yGOTMh9Q4mVSENS9FyweE/G+6ZsPDMCUU=
|
maunium.net/go/mautrix v0.26.0 h1:valc2VmZF+oIY4bMq4Cd5H9cEKMRe8eP4FM7iiaYLxI=
|
||||||
maunium.net/go/mautrix v0.25.2/go.mod h1:EWgYyp2iFZP7pnSm+rufHlO8YVnA2KnoNBDpwekiAwI=
|
maunium.net/go/mautrix v0.26.0/go.mod h1:NWMv+243NX/gDrLofJ2nNXJPrG8vzoM+WUCWph85S6Q=
|
||||||
modernc.org/cc/v4 v4.26.5 h1:xM3bX7Mve6G8K8b+T11ReenJOT+BmVqQj0FY5T4+5Y4=
|
modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=
|
||||||
modernc.org/cc/v4 v4.26.5/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||||
modernc.org/ccgo/v4 v4.28.1 h1:wPKYn5EC/mYTqBO373jKjvX2n+3+aK7+sICCv4Fjy1A=
|
modernc.org/ccgo/v4 v4.30.1 h1:4r4U1J6Fhj98NKfSjnPUN7Ze2c6MnAdL0hWw6+LrJpc=
|
||||||
modernc.org/ccgo/v4 v4.28.1/go.mod h1:uD+4RnfrVgE6ec9NGguUNdhqzNIeeomeXf6CL0GTE5Q=
|
modernc.org/ccgo/v4 v4.30.1/go.mod h1:bIOeI1JL54Utlxn+LwrFyjCx2n2RDiYEaJVSrgdrRfM=
|
||||||
modernc.org/fileutil v1.3.40 h1:ZGMswMNc9JOCrcrakF1HrvmergNLAmxOPjizirpfqBA=
|
modernc.org/fileutil v1.3.40 h1:ZGMswMNc9JOCrcrakF1HrvmergNLAmxOPjizirpfqBA=
|
||||||
modernc.org/fileutil v1.3.40/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
|
modernc.org/fileutil v1.3.40/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
|
||||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||||
|
modernc.org/gc/v3 v3.1.1 h1:k8T3gkXWY9sEiytKhcgyiZ2L0DTyCQ/nvX+LoCljoRE=
|
||||||
|
modernc.org/gc/v3 v3.1.1/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
|
||||||
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
|
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
|
||||||
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
|
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
|
||||||
modernc.org/libc v1.66.10 h1:yZkb3YeLx4oynyR+iUsXsybsX4Ubx7MQlSYEw4yj59A=
|
modernc.org/libc v1.67.1 h1:bFaqOaa5/zbWYJo8aW0tXPX21hXsngG2M7mckCnFSVk=
|
||||||
modernc.org/libc v1.66.10/go.mod h1:8vGSEwvoUoltr4dlywvHqjtAqHBaw0j1jI7iFBTAr2I=
|
modernc.org/libc v1.67.1/go.mod h1:QvvnnJ5P7aitu0ReNpVIEyesuhmDLQ8kaEoyMjIFZJA=
|
||||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||||
|
|
@ -242,8 +244,8 @@ modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
||||||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||||
modernc.org/sqlite v1.40.0 h1:bNWEDlYhNPAUdUdBzjAvn8icAs/2gaKlj4vM+tQ6KdQ=
|
modernc.org/sqlite v1.40.1 h1:VfuXcxcUWWKRBuP8+BR9L7VnmusMgBNNnBYGEe9w/iY=
|
||||||
modernc.org/sqlite v1.40.0/go.mod h1:9fjQZ0mB1LLP0GYrp39oOJXx/I2sxEnZtzCmEQIKvGE=
|
modernc.org/sqlite v1.40.1/go.mod h1:9fjQZ0mB1LLP0GYrp39oOJXx/I2sxEnZtzCmEQIKvGE=
|
||||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||||
|
|
|
||||||
1
vendor/github.com/clipperhouse/displaywidth/.gitignore
generated
vendored
Normal file
1
vendor/github.com/clipperhouse/displaywidth/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
.DS_Store
|
||||||
37
vendor/github.com/clipperhouse/displaywidth/AGENTS.md
generated
vendored
Normal file
37
vendor/github.com/clipperhouse/displaywidth/AGENTS.md
generated
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
The goals and overview of this package can be found in the README.md file,
|
||||||
|
start by reading that.
|
||||||
|
|
||||||
|
The goal of this package is to determine the display (column) width of a
|
||||||
|
string, UTF-8 bytes, or runes, as would happen in a monospace font, especially
|
||||||
|
in a terminal.
|
||||||
|
|
||||||
|
When troubleshooting, write Go unit tests instead of executing debug scripts.
|
||||||
|
The tests can return whatever logs or output you need. If those tests are
|
||||||
|
only for temporary troubleshooting, clean up the tests after the debugging is
|
||||||
|
done.
|
||||||
|
|
||||||
|
(Separate executable debugging scripts are messy, tend to have conflicting
|
||||||
|
dependencies and are hard to cleanup.)
|
||||||
|
|
||||||
|
If you make changes to the trie generation in internal/gen, it can be invoked
|
||||||
|
by running `go generate` from the top package directory.
|
||||||
|
|
||||||
|
## Pull Requests and branches
|
||||||
|
|
||||||
|
For PRs (pull requests), you can use the gh CLI tool to retrieve details,
|
||||||
|
or post comments. Then, compare the current branch with main. Reviewing a PR
|
||||||
|
and reviewing a branch are about the same, but the PR may add context.
|
||||||
|
|
||||||
|
Look for bugs. Think like GitHub Copilot or Cursor BugBot.
|
||||||
|
|
||||||
|
Offer to post a brief summary of the review to the PR, via the gh CLI tool.
|
||||||
|
|
||||||
|
## Comparisons to go-runewidth
|
||||||
|
|
||||||
|
We originally attempted to make this package compatible with go-runewidth.
|
||||||
|
However, we found that there were too many differences in the handling of
|
||||||
|
certain characters and properties.
|
||||||
|
|
||||||
|
We believe, preliminarily, that our choices are more correct and complete,
|
||||||
|
by using more complete categories such as Unicode Cf (format) for zero-width
|
||||||
|
and Mn (Nonspacing_Mark) for combining marks.
|
||||||
70
vendor/github.com/clipperhouse/displaywidth/CHANGELOG.md
generated
vendored
Normal file
70
vendor/github.com/clipperhouse/displaywidth/CHANGELOG.md
generated
vendored
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
## [0.6.1]
|
||||||
|
|
||||||
|
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.6.0...v0.6.1)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Perf improvements: replaced the ASCII lookup table with a simple
|
||||||
|
function. A bit more cache-friendly. More inlining.
|
||||||
|
- Bug fix: single regional indicators are now treated as width 2, since that
|
||||||
|
is what actual terminals do.
|
||||||
|
|
||||||
|
## [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
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2019 Oliver Kuederle
|
Copyright (c) 2025 Matt Sherman
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
165
vendor/github.com/clipperhouse/displaywidth/README.md
generated
vendored
Normal file
165
vendor/github.com/clipperhouse/displaywidth/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,165 @@
|
||||||
|
# displaywidth
|
||||||
|
|
||||||
|
A high-performance Go package for measuring the monospace display width of strings, UTF-8 bytes, and runes.
|
||||||
|
|
||||||
|
[](https://pkg.go.dev/github.com/clipperhouse/displaywidth)
|
||||||
|
[](https://github.com/clipperhouse/displaywidth/actions/workflows/gotest.yml)
|
||||||
|
[](https://github.com/clipperhouse/displaywidth/actions/workflows/gofuzz.yml)
|
||||||
|
|
||||||
|
## Install
|
||||||
|
```bash
|
||||||
|
go get github.com/clipperhouse/displaywidth
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/clipperhouse/displaywidth"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
width := displaywidth.String("Hello, 世界!")
|
||||||
|
fmt.Println(width)
|
||||||
|
|
||||||
|
width = displaywidth.Bytes([]byte("🌍"))
|
||||||
|
fmt.Println(width)
|
||||||
|
|
||||||
|
width = displaywidth.Rune('🌍')
|
||||||
|
fmt.Println(width)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
For most purposes, you should use the `String` or `Bytes` methods. They sum
|
||||||
|
the widths of grapheme clusters in the string or byte slice.
|
||||||
|
|
||||||
|
> Note: in your application, iterating over runes to measure width is likely incorrect;
|
||||||
|
the smallest unit of display is a grapheme, not a rune.
|
||||||
|
|
||||||
|
### Iterating over graphemes
|
||||||
|
|
||||||
|
If you need the individual graphemes:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/clipperhouse/displaywidth"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
g := displaywidth.StringGraphemes("Hello, 世界!")
|
||||||
|
for g.Next() {
|
||||||
|
width := g.Width()
|
||||||
|
value := g.Value()
|
||||||
|
// do something with the width or value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
There is one option, `displaywidth.Options.EastAsianWidth`, which defines
|
||||||
|
how [East Asian Ambiguous characters](https://www.unicode.org/reports/tr11/#Ambiguous)
|
||||||
|
are treated.
|
||||||
|
|
||||||
|
When `false` (default), East Asian Ambiguous characters are treated as width 1.
|
||||||
|
When `true`, they are treated as width 2.
|
||||||
|
|
||||||
|
You may wish to configure this based on environment variables or locale.
|
||||||
|
`go-runewidth`, for example, does so
|
||||||
|
[during package initialization](https://github.com/mattn/go-runewidth/blob/master/runewidth.go#L26C1-L45C2).
|
||||||
|
|
||||||
|
`displaywidth` does not do this automatically, we prefer to leave it to you.
|
||||||
|
You might do something like:
|
||||||
|
|
||||||
|
```go
|
||||||
|
var width displaywidth.Options // zero value is default
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if os.Getenv("EAST_ASIAN_WIDTH") == "true" {
|
||||||
|
width = displaywidth.Options{EastAsianWidth: true}
|
||||||
|
}
|
||||||
|
// or check locale, or any other logic you want
|
||||||
|
}
|
||||||
|
|
||||||
|
// use it in your logic
|
||||||
|
func myApp() {
|
||||||
|
fmt.Println(width.String("Hello, 世界!"))
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Technical standards and compatibility
|
||||||
|
|
||||||
|
This package implements the Unicode East Asian Width standard
|
||||||
|
([UAX #11](https://www.unicode.org/reports/tr11/tr11-43.html)), 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://www.unicode.org/reports/tr51/tr51-27.html). We are keeping
|
||||||
|
an eye on [emerging standards](https://www.jeffquast.com/post/state-of-terminal-emulation-2025/).
|
||||||
|
|
||||||
|
|
||||||
|
`clipperhouse/displaywidth`, `mattn/go-runewidth`, and `rivo/uniseg` will
|
||||||
|
give the same outputs for most real-world text. Extensive details are 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#L139). The essential trie generation logic is in
|
||||||
|
`buildPropertyBitmap` in [unicode.go](internal/gen/unicode.go#L316).
|
||||||
|
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd comparison
|
||||||
|
go test -bench=. -benchmem
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
goos: darwin
|
||||||
|
goarch: arm64
|
||||||
|
pkg: github.com/clipperhouse/displaywidth/comparison
|
||||||
|
cpu: Apple M2
|
||||||
|
|
||||||
|
BenchmarkString_Mixed/clipperhouse/displaywidth-8 10326 ns/op 163.37 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkString_Mixed/mattn/go-runewidth-8 14415 ns/op 117.03 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkString_Mixed/rivo/uniseg-8 19343 ns/op 87.21 MB/s 0 B/op 0 allocs/op
|
||||||
|
|
||||||
|
BenchmarkString_EastAsian/clipperhouse/displaywidth-8 10561 ns/op 159.74 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkString_EastAsian/mattn/go-runewidth-8 23790 ns/op 70.91 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkString_EastAsian/rivo/uniseg-8 19322 ns/op 87.31 MB/s 0 B/op 0 allocs/op
|
||||||
|
|
||||||
|
BenchmarkString_ASCII/clipperhouse/displaywidth-8 1033 ns/op 123.88 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkString_ASCII/mattn/go-runewidth-8 1168 ns/op 109.59 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkString_ASCII/rivo/uniseg-8 1585 ns/op 80.74 MB/s 0 B/op 0 allocs/op
|
||||||
|
|
||||||
|
BenchmarkString_Emoji/clipperhouse/displaywidth-8 3034 ns/op 238.61 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkString_Emoji/mattn/go-runewidth-8 4797 ns/op 150.94 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkString_Emoji/rivo/uniseg-8 6612 ns/op 109.50 MB/s 0 B/op 0 allocs/op
|
||||||
|
|
||||||
|
BenchmarkRune_Mixed/clipperhouse/displaywidth-8 3343 ns/op 504.67 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkRune_Mixed/mattn/go-runewidth-8 5414 ns/op 311.62 MB/s 0 B/op 0 allocs/op
|
||||||
|
|
||||||
|
BenchmarkRune_EastAsian/clipperhouse/displaywidth-8 3393 ns/op 497.17 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkRune_EastAsian/mattn/go-runewidth-8 15312 ns/op 110.17 MB/s 0 B/op 0 allocs/op
|
||||||
|
|
||||||
|
BenchmarkRune_ASCII/clipperhouse/displaywidth-8 256.9 ns/op 498.32 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkRune_ASCII/mattn/go-runewidth-8 265.7 ns/op 481.75 MB/s 0 B/op 0 allocs/op
|
||||||
|
|
||||||
|
BenchmarkRune_Emoji/clipperhouse/displaywidth-8 1336 ns/op 541.96 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkRune_Emoji/mattn/go-runewidth-8 2304 ns/op 314.23 MB/s 0 B/op 0 allocs/op
|
||||||
|
```
|
||||||
|
|
||||||
|
Here are some notes on [how to make Unicode things fast](https://clipperhouse.com/go-unicode/).
|
||||||
3
vendor/github.com/clipperhouse/displaywidth/gen.go
generated
vendored
Normal file
3
vendor/github.com/clipperhouse/displaywidth/gen.go
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
package displaywidth
|
||||||
|
|
||||||
|
//go:generate go run -C internal/gen .
|
||||||
72
vendor/github.com/clipperhouse/displaywidth/graphemes.go
generated
vendored
Normal file
72
vendor/github.com/clipperhouse/displaywidth/graphemes.go
generated
vendored
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
1724
vendor/github.com/clipperhouse/displaywidth/trie.go
generated
vendored
Normal file
1724
vendor/github.com/clipperhouse/displaywidth/trie.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
179
vendor/github.com/clipperhouse/displaywidth/width.go
generated
vendored
Normal file
179
vendor/github.com/clipperhouse/displaywidth/width.go
generated
vendored
Normal file
|
|
@ -0,0 +1,179 @@
|
||||||
|
package displaywidth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/clipperhouse/stringish"
|
||||||
|
"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.
|
||||||
|
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 asciiWidth(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.
|
||||||
|
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 asciiWidth(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 asciiWidth(byte(r))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Surrogates (U+D800-U+DFFF) are invalid UTF-8.
|
||||||
|
if r >= 0xD800 && r <= 0xDFFF {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf [4]byte
|
||||||
|
n := utf8.EncodeRune(buf[:], r)
|
||||||
|
|
||||||
|
// Skip the grapheme iterator
|
||||||
|
return graphemeWidth(buf[:n], options)
|
||||||
|
}
|
||||||
|
|
||||||
|
const _Default property = 0
|
||||||
|
|
||||||
|
// 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 asciiWidth(s[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
p, sz := lookup(s)
|
||||||
|
prop := property(p)
|
||||||
|
|
||||||
|
// Variation Selector 16 (VS16) requests emoji presentation
|
||||||
|
if sz > 0 && len(s) >= sz+3 {
|
||||||
|
vs := s[sz : sz+3]
|
||||||
|
if isVS16(vs) {
|
||||||
|
prop = _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.
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Note: we previously had some regional indicator handling here,
|
||||||
|
intending to treat single RI's as width 1 and pairs as width 2.
|
||||||
|
We think that's what the Unicode #11 indicates?
|
||||||
|
|
||||||
|
Then we looked at what actual terminals do, and they seem to treat
|
||||||
|
single and paired RI's as width 2, regardless. See terminal-test/.
|
||||||
|
Looks like VS Code does the same FWIW.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if options.EastAsianWidth && prop == _East_Asian_Ambiguous {
|
||||||
|
prop = _East_Asian_Wide
|
||||||
|
}
|
||||||
|
|
||||||
|
if prop > upperBound {
|
||||||
|
prop = _Default
|
||||||
|
}
|
||||||
|
|
||||||
|
return propertyWidths[prop]
|
||||||
|
}
|
||||||
|
|
||||||
|
func asciiWidth(b byte) int {
|
||||||
|
if b <= 0x1F || b == 0x7F {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// propertyWidths is a jump table of sorts, instead of a switch
|
||||||
|
var propertyWidths = [6]int{
|
||||||
|
_Default: 1,
|
||||||
|
_Zero_Width: 0,
|
||||||
|
_East_Asian_Wide: 2,
|
||||||
|
_East_Asian_Ambiguous: 1,
|
||||||
|
_Emoji: 2,
|
||||||
|
_Regional_Indicator: 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
const upperBound = property(len(propertyWidths) - 1)
|
||||||
35
vendor/github.com/etkecc/go-kit/utils.go
generated
vendored
35
vendor/github.com/etkecc/go-kit/utils.go
generated
vendored
|
|
@ -1,6 +1,9 @@
|
||||||
package kit
|
package kit
|
||||||
|
|
||||||
import "crypto/subtle"
|
import (
|
||||||
|
"crypto/subtle"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
// Eq checks if 2 strings are equal in constant time
|
// Eq checks if 2 strings are equal in constant time
|
||||||
func Eq(s1, s2 string) bool {
|
func Eq(s1, s2 string) bool {
|
||||||
|
|
@ -8,3 +11,33 @@ func Eq(s1, s2 string) bool {
|
||||||
b2 := []byte(s2)
|
b2 := []byte(s2)
|
||||||
return subtle.ConstantTimeEq(int32(len(b1)), int32(len(b2))) == 1 && subtle.ConstantTimeCompare(b1, b2) == 1 //nolint:gosec // that's ok
|
return subtle.ConstantTimeEq(int32(len(b1)), int32(len(b2))) == 1 && subtle.ConstantTimeCompare(b1, b2) == 1 //nolint:gosec // that's ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsNil checks if the given value is nil, including interfaces holding nil pointers
|
||||||
|
func IsNil(i any) bool {
|
||||||
|
// standard nil check
|
||||||
|
if i == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// special case: interfaces holding nil pointers (e.g. var x *int = nil; var y any = x)
|
||||||
|
rv := reflect.ValueOf(i)
|
||||||
|
for rv.Kind() == reflect.Pointer {
|
||||||
|
if rv.IsNil() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
rv = rv.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for other kinds that can be nil
|
||||||
|
switch rv.Kind() {
|
||||||
|
case reflect.Chan,
|
||||||
|
reflect.Func,
|
||||||
|
reflect.Map,
|
||||||
|
reflect.Pointer,
|
||||||
|
reflect.Slice,
|
||||||
|
reflect.UnsafePointer:
|
||||||
|
return rv.IsNil()
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
4
vendor/github.com/getsentry/sentry-go/.codecov.yml
generated
vendored
4
vendor/github.com/getsentry/sentry-go/.codecov.yml
generated
vendored
|
|
@ -11,5 +11,9 @@ coverage:
|
||||||
default:
|
default:
|
||||||
# Do not fail the commit status if the coverage was reduced up to this value
|
# Do not fail the commit status if the coverage was reduced up to this value
|
||||||
threshold: 0.5%
|
threshold: 0.5%
|
||||||
|
patch:
|
||||||
|
default:
|
||||||
|
informational: true
|
||||||
ignore:
|
ignore:
|
||||||
- "log_fallback.go"
|
- "log_fallback.go"
|
||||||
|
- "internal/testutils"
|
||||||
|
|
|
||||||
66
vendor/github.com/getsentry/sentry-go/CHANGELOG.md
generated
vendored
66
vendor/github.com/getsentry/sentry-go/CHANGELOG.md
generated
vendored
|
|
@ -1,5 +1,71 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.40.0
|
||||||
|
|
||||||
|
The Sentry SDK team is happy to announce the immediate availability of Sentry Go SDK v0.40.0.
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Disable `DisableTelemetryBuffer` flag and noop Telemetry Buffer, to prevent a panic at runtime ([#1149](https://github.com/getsentry/sentry-go/pull/1149)).
|
||||||
|
|
||||||
|
## 0.39.0
|
||||||
|
|
||||||
|
The Sentry SDK team is happy to announce the immediate availability of Sentry Go SDK v0.39.0.
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Drop events from the telemetry buffer when rate-limited or transport is full, allowing the buffer queue to empty itself under load ([#1138](https://github.com/getsentry/sentry-go/pull/1138)).
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Fix scheduler's `hasWork()` method to check if buffers are ready to flush. The previous implementation was causing CPU spikes ([#1143](https://github.com/getsentry/sentry-go/pull/1143)).
|
||||||
|
|
||||||
|
## 0.38.0
|
||||||
|
|
||||||
|
### Breaking Changes
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Introduce a new async envelope transport and telemetry buffer to prioritize and batch events ([#1094](https://github.com/getsentry/sentry-go/pull/1094), [#1093](https://github.com/getsentry/sentry-go/pull/1093), [#1107](https://github.com/getsentry/sentry-go/pull/1107)).
|
||||||
|
- Advantages:
|
||||||
|
- Prioritized, per-category buffers (errors, transactions, logs, check-ins) reduce starvation and improve resilience under load
|
||||||
|
- Batching for high-volume logs (up to 100 items or 5s) cuts network overhead
|
||||||
|
- Bounded memory with eviction policies
|
||||||
|
- Improved flush behavior with context-aware flushing
|
||||||
|
- Add `ClientOptions.DisableTelemetryBuffer` to opt out and fall back to the legacy transport layer (`HTTPTransport` / `HTTPSyncTransport`).
|
||||||
|
|
||||||
|
```go
|
||||||
|
err := sentry.Init(sentry.ClientOptions{
|
||||||
|
Dsn: "__DSN__",
|
||||||
|
DisableTelemetryBuffer: true, // fallback to legacy transport
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
- If a custom `Transport` is provided, the SDK automatically disables the telemetry buffer and uses the legacy transport for compatibility.
|
||||||
|
|
||||||
|
## 0.37.0
|
||||||
|
|
||||||
|
The Sentry SDK team is happy to announce the immediate availability of Sentry Go SDK v0.37.0.
|
||||||
|
|
||||||
|
### Breaking Changes
|
||||||
|
|
||||||
|
- Behavioral change for the `TraceIgnoreStatusCodes` option. The option now defaults to ignoring 404 status codes ([#1122](https://github.com/getsentry/sentry-go/pull/1122)).
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Add `sentry.origin` attribute to structured logs to identify log origin for `slog` and `logrus` integrations (`auto.log.slog`, `auto.log.logrus`) ([#1121](https://github.com/getsentry/sentry-go/pull/1121)).
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Fix `slog` event handler to use the initial context, ensuring events use the correct hub/span when the emission context lacks one ([#1133](https://github.com/getsentry/sentry-go/pull/1133)).
|
||||||
|
- Improve exception chain processing by checking pointer values when tracking visited errors, avoiding instability for certain wrapped errors ([#1132](https://github.com/getsentry/sentry-go/pull/1132)).
|
||||||
|
|
||||||
|
### Misc
|
||||||
|
|
||||||
|
- Bump `golang.org/x/net` to v0.38.0 ([#1126](https://github.com/getsentry/sentry-go/pull/1126)).
|
||||||
|
|
||||||
## 0.36.2
|
## 0.36.2
|
||||||
|
|
||||||
The Sentry SDK team is happy to announce the immediate availability of Sentry Go SDK v0.36.2.
|
The Sentry SDK team is happy to announce the immediate availability of Sentry Go SDK v0.36.2.
|
||||||
|
|
|
||||||
2
vendor/github.com/getsentry/sentry-go/Makefile
generated
vendored
2
vendor/github.com/getsentry/sentry-go/Makefile
generated
vendored
|
|
@ -70,7 +70,7 @@ mod-tidy: ## Check go.mod tidiness
|
||||||
set -e ; \
|
set -e ; \
|
||||||
for dir in $(ALL_GO_MOD_DIRS); do \
|
for dir in $(ALL_GO_MOD_DIRS); do \
|
||||||
echo ">>> Running 'go mod tidy' for module: $${dir}"; \
|
echo ">>> Running 'go mod tidy' for module: $${dir}"; \
|
||||||
(cd "$${dir}" && go mod tidy -go=1.23 -compat=1.23); \
|
(cd "$${dir}" && GOTOOLCHAIN=local go mod tidy -go=1.23.0 -compat=1.23); \
|
||||||
done; \
|
done; \
|
||||||
git diff --exit-code;
|
git diff --exit-code;
|
||||||
.PHONY: mod-tidy
|
.PHONY: mod-tidy
|
||||||
|
|
|
||||||
1
vendor/github.com/getsentry/sentry-go/README.md
generated
vendored
1
vendor/github.com/getsentry/sentry-go/README.md
generated
vendored
|
|
@ -13,6 +13,7 @@
|
||||||
[](https://github.com/getsentry/sentry-go/actions/workflows/test.yml)
|
[](https://github.com/getsentry/sentry-go/actions/workflows/test.yml)
|
||||||
[](https://goreportcard.com/report/github.com/getsentry/sentry-go)
|
[](https://goreportcard.com/report/github.com/getsentry/sentry-go)
|
||||||
[](https://discord.gg/Ww9hbqr)
|
[](https://discord.gg/Ww9hbqr)
|
||||||
|
[](https://x.com/intent/follow?screen_name=sentry)
|
||||||
[](https://pkg.go.dev/github.com/getsentry/sentry-go)
|
[](https://pkg.go.dev/github.com/getsentry/sentry-go)
|
||||||
|
|
||||||
`sentry-go` provides a Sentry client implementation for the Go programming
|
`sentry-go` provides a Sentry client implementation for the Go programming
|
||||||
|
|
|
||||||
92
vendor/github.com/getsentry/sentry-go/client.go
generated
vendored
92
vendor/github.com/getsentry/sentry-go/client.go
generated
vendored
|
|
@ -15,6 +15,10 @@ import (
|
||||||
|
|
||||||
"github.com/getsentry/sentry-go/internal/debug"
|
"github.com/getsentry/sentry-go/internal/debug"
|
||||||
"github.com/getsentry/sentry-go/internal/debuglog"
|
"github.com/getsentry/sentry-go/internal/debuglog"
|
||||||
|
httpInternal "github.com/getsentry/sentry-go/internal/http"
|
||||||
|
"github.com/getsentry/sentry-go/internal/protocol"
|
||||||
|
"github.com/getsentry/sentry-go/internal/ratelimit"
|
||||||
|
"github.com/getsentry/sentry-go/internal/telemetry"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The identifier of the SDK.
|
// The identifier of the SDK.
|
||||||
|
|
@ -247,8 +251,13 @@ type ClientOptions struct {
|
||||||
// [][]int{{404}, {500}} // ignore status codes 404 and 500
|
// [][]int{{404}, {500}} // ignore status codes 404 and 500
|
||||||
// [][]int{{404}, {400, 405}, {500, 599}} // ignore 404, range 400-405, and range 500-599
|
// [][]int{{404}, {400, 405}, {500, 599}} // ignore 404, range 400-405, and range 500-599
|
||||||
//
|
//
|
||||||
// By default, this is empty and all status codes are traced.
|
// By default, this ignores 404 status codes.
|
||||||
|
//
|
||||||
|
// IMPORTANT: to not ignore any status codes, the option should be an empty slice and not nil. The nil option is
|
||||||
|
// used for defaulting to 404 ignores.
|
||||||
TraceIgnoreStatusCodes [][]int
|
TraceIgnoreStatusCodes [][]int
|
||||||
|
// DisableTelemetryBuffer disables the telemetry buffer layer for prioritizing events and uses the old transport layer.
|
||||||
|
DisableTelemetryBuffer bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client is the underlying processor that is used by the main API and Hub
|
// Client is the underlying processor that is used by the main API and Hub
|
||||||
|
|
@ -263,8 +272,9 @@ type Client struct {
|
||||||
sdkVersion string
|
sdkVersion string
|
||||||
// Transport is read-only. Replacing the transport of an existing client is
|
// Transport is read-only. Replacing the transport of an existing client is
|
||||||
// not supported, create a new client instead.
|
// not supported, create a new client instead.
|
||||||
Transport Transport
|
Transport Transport
|
||||||
batchLogger *BatchLogger
|
batchLogger *BatchLogger
|
||||||
|
telemetryBuffer *telemetry.Buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClient creates and returns an instance of Client configured using
|
// NewClient creates and returns an instance of Client configured using
|
||||||
|
|
@ -325,6 +335,10 @@ func NewClient(options ClientOptions) (*Client, error) {
|
||||||
options.MaxSpans = defaultMaxSpans
|
options.MaxSpans = defaultMaxSpans
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if options.TraceIgnoreStatusCodes == nil {
|
||||||
|
options.TraceIgnoreStatusCodes = [][]int{{404}}
|
||||||
|
}
|
||||||
|
|
||||||
// SENTRYGODEBUG is a comma-separated list of key=value pairs (similar
|
// SENTRYGODEBUG is a comma-separated list of key=value pairs (similar
|
||||||
// to GODEBUG). It is not a supported feature: recognized debug options
|
// to GODEBUG). It is not a supported feature: recognized debug options
|
||||||
// may change any time.
|
// may change any time.
|
||||||
|
|
@ -364,12 +378,17 @@ func NewClient(options ClientOptions) (*Client, error) {
|
||||||
sdkVersion: SDKVersion,
|
sdkVersion: SDKVersion,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client.setupTransport()
|
||||||
|
|
||||||
|
// noop Telemetry Buffers fow now
|
||||||
|
// if !options.DisableTelemetryBuffer {
|
||||||
|
// client.setupTelemetryBuffer()
|
||||||
|
// } else
|
||||||
if options.EnableLogs {
|
if options.EnableLogs {
|
||||||
client.batchLogger = NewBatchLogger(&client)
|
client.batchLogger = NewBatchLogger(&client)
|
||||||
client.batchLogger.Start()
|
client.batchLogger.Start()
|
||||||
}
|
}
|
||||||
|
|
||||||
client.setupTransport()
|
|
||||||
client.setupIntegrations()
|
client.setupIntegrations()
|
||||||
|
|
||||||
return &client, nil
|
return &client, nil
|
||||||
|
|
@ -391,6 +410,52 @@ func (client *Client) setupTransport() {
|
||||||
client.Transport = transport
|
client.Transport = transport
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (client *Client) setupTelemetryBuffer() { // nolint: unused
|
||||||
|
if client.options.DisableTelemetryBuffer {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if client.dsn == nil {
|
||||||
|
debuglog.Println("Telemetry buffer disabled: no DSN configured")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// We currently disallow using custom Transport with the new Telemetry Buffer, due to the difference in transport signatures.
|
||||||
|
// The option should be enabled when the new Transport interface signature changes.
|
||||||
|
if client.options.Transport != nil {
|
||||||
|
debuglog.Println("Cannot enable Telemetry Buffer with custom Transport: fallback to old transport")
|
||||||
|
if client.options.EnableLogs {
|
||||||
|
client.batchLogger = NewBatchLogger(client)
|
||||||
|
client.batchLogger.Start()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
transport := httpInternal.NewAsyncTransport(httpInternal.TransportOptions{
|
||||||
|
Dsn: client.options.Dsn,
|
||||||
|
HTTPClient: client.options.HTTPClient,
|
||||||
|
HTTPTransport: client.options.HTTPTransport,
|
||||||
|
HTTPProxy: client.options.HTTPProxy,
|
||||||
|
HTTPSProxy: client.options.HTTPSProxy,
|
||||||
|
CaCerts: client.options.CaCerts,
|
||||||
|
})
|
||||||
|
client.Transport = &internalAsyncTransportAdapter{transport: transport}
|
||||||
|
|
||||||
|
storage := map[ratelimit.Category]telemetry.Storage[protocol.EnvelopeItemConvertible]{
|
||||||
|
ratelimit.CategoryError: telemetry.NewRingBuffer[protocol.EnvelopeItemConvertible](ratelimit.CategoryError, 100, telemetry.OverflowPolicyDropOldest, 1, 0),
|
||||||
|
ratelimit.CategoryTransaction: telemetry.NewRingBuffer[protocol.EnvelopeItemConvertible](ratelimit.CategoryTransaction, 1000, telemetry.OverflowPolicyDropOldest, 1, 0),
|
||||||
|
ratelimit.CategoryLog: telemetry.NewRingBuffer[protocol.EnvelopeItemConvertible](ratelimit.CategoryLog, 10*100, telemetry.OverflowPolicyDropOldest, 100, 5*time.Second),
|
||||||
|
ratelimit.CategoryMonitor: telemetry.NewRingBuffer[protocol.EnvelopeItemConvertible](ratelimit.CategoryMonitor, 100, telemetry.OverflowPolicyDropOldest, 1, 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
sdkInfo := &protocol.SdkInfo{
|
||||||
|
Name: client.sdkIdentifier,
|
||||||
|
Version: client.sdkVersion,
|
||||||
|
}
|
||||||
|
|
||||||
|
client.telemetryBuffer = telemetry.NewBuffer(storage, transport, &client.dsn.Dsn, sdkInfo)
|
||||||
|
}
|
||||||
|
|
||||||
func (client *Client) setupIntegrations() {
|
func (client *Client) setupIntegrations() {
|
||||||
integrations := []Integration{
|
integrations := []Integration{
|
||||||
new(contextifyFramesIntegration),
|
new(contextifyFramesIntegration),
|
||||||
|
|
@ -531,7 +596,7 @@ func (client *Client) RecoverWithContext(
|
||||||
// the network synchronously, configure it to use the HTTPSyncTransport in the
|
// the network synchronously, configure it to use the HTTPSyncTransport in the
|
||||||
// call to Init.
|
// call to Init.
|
||||||
func (client *Client) Flush(timeout time.Duration) bool {
|
func (client *Client) Flush(timeout time.Duration) bool {
|
||||||
if client.batchLogger != nil {
|
if client.batchLogger != nil || client.telemetryBuffer != nil {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
return client.FlushWithContext(ctx)
|
return client.FlushWithContext(ctx)
|
||||||
|
|
@ -555,6 +620,9 @@ func (client *Client) FlushWithContext(ctx context.Context) bool {
|
||||||
if client.batchLogger != nil {
|
if client.batchLogger != nil {
|
||||||
client.batchLogger.Flush(ctx.Done())
|
client.batchLogger.Flush(ctx.Done())
|
||||||
}
|
}
|
||||||
|
if client.telemetryBuffer != nil {
|
||||||
|
return client.telemetryBuffer.FlushWithContext(ctx)
|
||||||
|
}
|
||||||
return client.Transport.FlushWithContext(ctx)
|
return client.Transport.FlushWithContext(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -563,6 +631,12 @@ func (client *Client) FlushWithContext(ctx context.Context) bool {
|
||||||
// Close should be called after Flush and before terminating the program
|
// Close should be called after Flush and before terminating the program
|
||||||
// otherwise some events may be lost.
|
// otherwise some events may be lost.
|
||||||
func (client *Client) Close() {
|
func (client *Client) Close() {
|
||||||
|
if client.telemetryBuffer != nil {
|
||||||
|
client.telemetryBuffer.Close(5 * time.Second)
|
||||||
|
}
|
||||||
|
if client.batchLogger != nil {
|
||||||
|
client.batchLogger.Shutdown()
|
||||||
|
}
|
||||||
client.Transport.Close()
|
client.Transport.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -683,7 +757,13 @@ func (client *Client) processEvent(event *Event, hint *EventHint, scope EventMod
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
client.Transport.SendEvent(event)
|
if client.telemetryBuffer != nil {
|
||||||
|
if !client.telemetryBuffer.Add(event) {
|
||||||
|
debuglog.Println("Event dropped: telemetry buffer full or unavailable")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
client.Transport.SendEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
return &event.EventID
|
return &event.EventID
|
||||||
}
|
}
|
||||||
|
|
|
||||||
21
vendor/github.com/getsentry/sentry-go/exception.go
generated
vendored
21
vendor/github.com/getsentry/sentry-go/exception.go
generated
vendored
|
|
@ -14,21 +14,18 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type visited struct {
|
type visited struct {
|
||||||
comparable map[error]struct{}
|
ptrs map[uintptr]struct{}
|
||||||
msgs map[string]struct{}
|
msgs map[string]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *visited) seenError(err error) bool {
|
func (v *visited) seenError(err error) bool {
|
||||||
t := reflect.TypeOf(err)
|
t := reflect.ValueOf(err)
|
||||||
if t == nil {
|
if t.Kind() == reflect.Ptr && !t.IsNil() {
|
||||||
return false
|
ptr := t.Pointer()
|
||||||
}
|
if _, ok := v.ptrs[ptr]; ok {
|
||||||
|
|
||||||
if t.Comparable() {
|
|
||||||
if _, ok := v.comparable[err]; ok {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
v.comparable[err] = struct{}{}
|
v.ptrs[ptr] = struct{}{}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,8 +40,8 @@ func (v *visited) seenError(err error) bool {
|
||||||
func convertErrorToExceptions(err error, maxErrorDepth int) []Exception {
|
func convertErrorToExceptions(err error, maxErrorDepth int) []Exception {
|
||||||
var exceptions []Exception
|
var exceptions []Exception
|
||||||
vis := &visited{
|
vis := &visited{
|
||||||
make(map[error]struct{}),
|
ptrs: make(map[uintptr]struct{}),
|
||||||
make(map[string]struct{}),
|
msgs: make(map[string]struct{}),
|
||||||
}
|
}
|
||||||
convertErrorDFS(err, &exceptions, nil, "", vis, maxErrorDepth, 0)
|
convertErrorDFS(err, &exceptions, nil, "", vis, maxErrorDepth, 0)
|
||||||
|
|
||||||
|
|
|
||||||
127
vendor/github.com/getsentry/sentry-go/interfaces.go
generated
vendored
127
vendor/github.com/getsentry/sentry-go/interfaces.go
generated
vendored
|
|
@ -493,6 +493,70 @@ func (e *Event) ToEnvelopeWithTime(dsn *protocol.Dsn, sentAt time.Time) (*protoc
|
||||||
return envelope, nil
|
return envelope, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToEnvelopeItem converts the Event to a Sentry envelope item.
|
||||||
|
func (e *Event) ToEnvelopeItem() (*protocol.EnvelopeItem, error) {
|
||||||
|
eventBody, err := json.Marshal(e)
|
||||||
|
if err != nil {
|
||||||
|
// Try fallback: remove problematic fields and retry
|
||||||
|
e.Breadcrumbs = nil
|
||||||
|
e.Contexts = nil
|
||||||
|
e.Extra = map[string]interface{}{
|
||||||
|
"info": fmt.Sprintf("Could not encode original event as JSON. "+
|
||||||
|
"Succeeded by removing Breadcrumbs, Contexts and Extra. "+
|
||||||
|
"Please verify the data you attach to the scope. "+
|
||||||
|
"Error: %s", err),
|
||||||
|
}
|
||||||
|
|
||||||
|
eventBody, err = json.Marshal(e)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("event could not be marshaled even with fallback: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugLogger.Printf("Event marshaling succeeded with fallback after removing problematic fields")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: all event types should be abstracted to implement EnvelopeItemConvertible and convert themselves.
|
||||||
|
var item *protocol.EnvelopeItem
|
||||||
|
switch e.Type {
|
||||||
|
case transactionType:
|
||||||
|
item = protocol.NewEnvelopeItem(protocol.EnvelopeItemTypeTransaction, eventBody)
|
||||||
|
case checkInType:
|
||||||
|
item = protocol.NewEnvelopeItem(protocol.EnvelopeItemTypeCheckIn, eventBody)
|
||||||
|
case logEvent.Type:
|
||||||
|
item = protocol.NewLogItem(len(e.Logs), eventBody)
|
||||||
|
default:
|
||||||
|
item = protocol.NewEnvelopeItem(protocol.EnvelopeItemTypeEvent, eventBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
return item, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCategory returns the rate limit category for this event.
|
||||||
|
func (e *Event) GetCategory() ratelimit.Category {
|
||||||
|
return e.toCategory()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEventID returns the event ID.
|
||||||
|
func (e *Event) GetEventID() string {
|
||||||
|
return string(e.EventID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSdkInfo returns SDK information for the envelope header.
|
||||||
|
func (e *Event) GetSdkInfo() *protocol.SdkInfo {
|
||||||
|
return &e.Sdk
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDynamicSamplingContext returns trace context for the envelope header.
|
||||||
|
func (e *Event) GetDynamicSamplingContext() map[string]string {
|
||||||
|
trace := make(map[string]string)
|
||||||
|
if dsc := e.sdkMetaData.dsc; dsc.HasEntries() {
|
||||||
|
for k, v := range dsc.Entries {
|
||||||
|
trace[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return trace
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Event.Contexts map[string]interface{} => map[string]EventContext,
|
// TODO: Event.Contexts map[string]interface{} => map[string]EventContext,
|
||||||
// to prevent accidentally storing T when we mean *T.
|
// to prevent accidentally storing T when we mean *T.
|
||||||
// For example, the TraceContext must be stored as *TraceContext to pick up the
|
// For example, the TraceContext must be stored as *TraceContext to pick up the
|
||||||
|
|
@ -667,6 +731,69 @@ type Log struct {
|
||||||
Attributes map[string]Attribute `json:"attributes,omitempty"`
|
Attributes map[string]Attribute `json:"attributes,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToEnvelopeItem converts the Log to a Sentry envelope item for batching.
|
||||||
|
func (l *Log) ToEnvelopeItem() (*protocol.EnvelopeItem, error) {
|
||||||
|
type logJSON struct {
|
||||||
|
Timestamp *float64 `json:"timestamp,omitempty"`
|
||||||
|
TraceID string `json:"trace_id,omitempty"`
|
||||||
|
Level string `json:"level"`
|
||||||
|
Severity int `json:"severity_number,omitempty"`
|
||||||
|
Body string `json:"body,omitempty"`
|
||||||
|
Attributes map[string]protocol.LogAttribute `json:"attributes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert time.Time to seconds float if set
|
||||||
|
var ts *float64
|
||||||
|
if !l.Timestamp.IsZero() {
|
||||||
|
sec := float64(l.Timestamp.UnixNano()) / 1e9
|
||||||
|
ts = &sec
|
||||||
|
}
|
||||||
|
|
||||||
|
attrs := make(map[string]protocol.LogAttribute, len(l.Attributes))
|
||||||
|
for k, v := range l.Attributes {
|
||||||
|
attrs[k] = protocol.LogAttribute{Value: v.Value, Type: string(v.Type)}
|
||||||
|
}
|
||||||
|
|
||||||
|
logData, err := json.Marshal(logJSON{
|
||||||
|
Timestamp: ts,
|
||||||
|
TraceID: l.TraceID.String(),
|
||||||
|
Level: string(l.Level),
|
||||||
|
Severity: l.Severity,
|
||||||
|
Body: l.Body,
|
||||||
|
Attributes: attrs,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &protocol.EnvelopeItem{
|
||||||
|
Header: &protocol.EnvelopeItemHeader{
|
||||||
|
Type: protocol.EnvelopeItemTypeLog,
|
||||||
|
},
|
||||||
|
Payload: logData,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCategory returns the rate limit category for logs.
|
||||||
|
func (l *Log) GetCategory() ratelimit.Category {
|
||||||
|
return ratelimit.CategoryLog
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEventID returns empty string (event ID set when batching).
|
||||||
|
func (l *Log) GetEventID() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSdkInfo returns nil (SDK info set when batching).
|
||||||
|
func (l *Log) GetSdkInfo() *protocol.SdkInfo {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDynamicSamplingContext returns nil (trace context set when batching).
|
||||||
|
func (l *Log) GetDynamicSamplingContext() map[string]string {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type AttrType string
|
type AttrType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
||||||
83
vendor/github.com/getsentry/sentry-go/internal/http/transport.go
generated
vendored
83
vendor/github.com/getsentry/sentry-go/internal/http/transport.go
generated
vendored
|
|
@ -33,6 +33,7 @@ const (
|
||||||
var (
|
var (
|
||||||
ErrTransportQueueFull = errors.New("transport queue full")
|
ErrTransportQueueFull = errors.New("transport queue full")
|
||||||
ErrTransportClosed = errors.New("transport is closed")
|
ErrTransportClosed = errors.New("transport is closed")
|
||||||
|
ErrEmptyEnvelope = errors.New("empty envelope provided")
|
||||||
)
|
)
|
||||||
|
|
||||||
type TransportOptions struct {
|
type TransportOptions struct {
|
||||||
|
|
@ -196,31 +197,15 @@ func (t *SyncTransport) SendEnvelope(envelope *protocol.Envelope) error {
|
||||||
|
|
||||||
func (t *SyncTransport) Close() {}
|
func (t *SyncTransport) Close() {}
|
||||||
|
|
||||||
func (t *SyncTransport) SendEvent(event protocol.EnvelopeConvertible) {
|
|
||||||
envelope, err := event.ToEnvelope(t.dsn)
|
|
||||||
if err != nil {
|
|
||||||
debuglog.Printf("Failed to convert to envelope: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if envelope == nil {
|
|
||||||
debuglog.Printf("Error: event with empty envelope")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := t.SendEnvelope(envelope); err != nil {
|
|
||||||
debuglog.Printf("Error sending the envelope: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *SyncTransport) IsRateLimited(category ratelimit.Category) bool {
|
func (t *SyncTransport) IsRateLimited(category ratelimit.Category) bool {
|
||||||
return t.disabled(category)
|
return t.disabled(category)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *SyncTransport) HasCapacity() bool { return true }
|
||||||
|
|
||||||
func (t *SyncTransport) SendEnvelopeWithContext(ctx context.Context, envelope *protocol.Envelope) error {
|
func (t *SyncTransport) SendEnvelopeWithContext(ctx context.Context, envelope *protocol.Envelope) error {
|
||||||
if envelope == nil {
|
if envelope == nil || len(envelope.Items) == 0 {
|
||||||
debuglog.Printf("Error: provided empty envelope")
|
return ErrEmptyEnvelope
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
category := categoryFromEnvelope(envelope)
|
category := categoryFromEnvelope(envelope)
|
||||||
|
|
@ -233,6 +218,14 @@ func (t *SyncTransport) SendEnvelopeWithContext(ctx context.Context, envelope *p
|
||||||
debuglog.Printf("There was an issue creating the request: %v", err)
|
debuglog.Printf("There was an issue creating the request: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
debuglog.Printf(
|
||||||
|
"Sending %s [%s] to %s project: %s",
|
||||||
|
envelope.Items[0].Header.Type,
|
||||||
|
envelope.Header.EventID,
|
||||||
|
t.dsn.GetHost(),
|
||||||
|
t.dsn.GetProjectID(),
|
||||||
|
)
|
||||||
|
|
||||||
response, err := t.client.Do(request)
|
response, err := t.client.Do(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
debuglog.Printf("There was an issue with sending an event: %v", err)
|
debuglog.Printf("There was an issue with sending an event: %v", err)
|
||||||
|
|
@ -354,6 +347,19 @@ func (t *AsyncTransport) start() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasCapacity reports whether the async transport queue appears to have space
|
||||||
|
// for at least one more envelope. This is a best-effort, non-blocking check.
|
||||||
|
func (t *AsyncTransport) HasCapacity() bool {
|
||||||
|
t.mu.RLock()
|
||||||
|
defer t.mu.RUnlock()
|
||||||
|
select {
|
||||||
|
case <-t.done:
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return len(t.queue) < cap(t.queue)
|
||||||
|
}
|
||||||
|
|
||||||
func (t *AsyncTransport) SendEnvelope(envelope *protocol.Envelope) error {
|
func (t *AsyncTransport) SendEnvelope(envelope *protocol.Envelope) error {
|
||||||
select {
|
select {
|
||||||
case <-t.done:
|
case <-t.done:
|
||||||
|
|
@ -361,6 +367,10 @@ func (t *AsyncTransport) SendEnvelope(envelope *protocol.Envelope) error {
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if envelope == nil || len(envelope.Items) == 0 {
|
||||||
|
return ErrEmptyEnvelope
|
||||||
|
}
|
||||||
|
|
||||||
category := categoryFromEnvelope(envelope)
|
category := categoryFromEnvelope(envelope)
|
||||||
if t.isRateLimited(category) {
|
if t.isRateLimited(category) {
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -368,6 +378,13 @@ func (t *AsyncTransport) SendEnvelope(envelope *protocol.Envelope) error {
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case t.queue <- envelope:
|
case t.queue <- envelope:
|
||||||
|
debuglog.Printf(
|
||||||
|
"Sending %s [%s] to %s project: %s",
|
||||||
|
envelope.Items[0].Header.Type,
|
||||||
|
envelope.Header.EventID,
|
||||||
|
t.dsn.GetHost(),
|
||||||
|
t.dsn.GetProjectID(),
|
||||||
|
)
|
||||||
return nil
|
return nil
|
||||||
default:
|
default:
|
||||||
atomic.AddInt64(&t.droppedCount, 1)
|
atomic.AddInt64(&t.droppedCount, 1)
|
||||||
|
|
@ -375,23 +392,6 @@ func (t *AsyncTransport) SendEnvelope(envelope *protocol.Envelope) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *AsyncTransport) SendEvent(event protocol.EnvelopeConvertible) {
|
|
||||||
envelope, err := event.ToEnvelope(t.dsn)
|
|
||||||
if err != nil {
|
|
||||||
debuglog.Printf("Failed to convert to envelope: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if envelope == nil {
|
|
||||||
debuglog.Printf("Error: event with empty envelope")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := t.SendEnvelope(envelope); err != nil {
|
|
||||||
debuglog.Printf("Error sending the envelope: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *AsyncTransport) Flush(timeout time.Duration) bool {
|
func (t *AsyncTransport) Flush(timeout time.Duration) bool {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
@ -404,11 +404,14 @@ func (t *AsyncTransport) FlushWithContext(ctx context.Context) bool {
|
||||||
case t.flushRequest <- flushResponse:
|
case t.flushRequest <- flushResponse:
|
||||||
select {
|
select {
|
||||||
case <-flushResponse:
|
case <-flushResponse:
|
||||||
|
debuglog.Println("Buffer flushed successfully.")
|
||||||
return true
|
return true
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
|
debuglog.Println("Failed to flush, buffer timed out.")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
|
debuglog.Println("Failed to flush, buffer timed out.")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -550,10 +553,6 @@ func (t *NoopTransport) SendEnvelope(_ *protocol.Envelope) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *NoopTransport) SendEvent(_ protocol.EnvelopeConvertible) {
|
|
||||||
debuglog.Println("Event dropped due to NoopTransport usage.")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *NoopTransport) IsRateLimited(_ ratelimit.Category) bool {
|
func (t *NoopTransport) IsRateLimited(_ ratelimit.Category) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
@ -569,3 +568,5 @@ func (t *NoopTransport) FlushWithContext(_ context.Context) bool {
|
||||||
func (t *NoopTransport) Close() {
|
func (t *NoopTransport) Close() {
|
||||||
// Nothing to close
|
// Nothing to close
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *NoopTransport) HasCapacity() bool { return true }
|
||||||
|
|
|
||||||
27
vendor/github.com/getsentry/sentry-go/internal/protocol/interfaces.go
generated
vendored
27
vendor/github.com/getsentry/sentry-go/internal/protocol/interfaces.go
generated
vendored
|
|
@ -7,12 +7,23 @@ import (
|
||||||
"github.com/getsentry/sentry-go/internal/ratelimit"
|
"github.com/getsentry/sentry-go/internal/ratelimit"
|
||||||
)
|
)
|
||||||
|
|
||||||
// EnvelopeConvertible represents any type that can be converted to a Sentry envelope.
|
// EnvelopeItemConvertible represents any type that can be converted to a Sentry envelope item.
|
||||||
// This interface allows the telemetry buffers to be generic while still working with
|
// This interface allows the telemetry buffers to work with items that can be batched together.
|
||||||
// concrete types like Event.
|
type EnvelopeItemConvertible interface {
|
||||||
type EnvelopeConvertible interface {
|
// ToEnvelopeItem converts the item to a Sentry envelope item.
|
||||||
// ToEnvelope converts the item to a Sentry envelope.
|
ToEnvelopeItem() (*EnvelopeItem, error)
|
||||||
ToEnvelope(dsn *Dsn) (*Envelope, error)
|
|
||||||
|
// GetCategory returns the rate limit category for this item.
|
||||||
|
GetCategory() ratelimit.Category
|
||||||
|
|
||||||
|
// GetEventID returns the event ID for this item.
|
||||||
|
GetEventID() string
|
||||||
|
|
||||||
|
// GetSdkInfo returns SDK information for the envelope header.
|
||||||
|
GetSdkInfo() *SdkInfo
|
||||||
|
|
||||||
|
// GetDynamicSamplingContext returns trace context for the envelope header.
|
||||||
|
GetDynamicSamplingContext() map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
// TelemetryTransport represents the envelope-first transport interface.
|
// TelemetryTransport represents the envelope-first transport interface.
|
||||||
|
|
@ -23,8 +34,8 @@ type TelemetryTransport interface {
|
||||||
// backpressure error if the queue is full.
|
// backpressure error if the queue is full.
|
||||||
SendEnvelope(envelope *Envelope) error
|
SendEnvelope(envelope *Envelope) error
|
||||||
|
|
||||||
// SendEvent sends an event to Sentry.
|
// HasCapacity reports whether the transport has capacity to accept at least one more envelope.
|
||||||
SendEvent(event EnvelopeConvertible)
|
HasCapacity() bool
|
||||||
|
|
||||||
// IsRateLimited checks if a specific category is currently rate limited
|
// IsRateLimited checks if a specific category is currently rate limited
|
||||||
IsRateLimited(category ratelimit.Category) bool
|
IsRateLimited(category ratelimit.Category) bool
|
||||||
|
|
|
||||||
48
vendor/github.com/getsentry/sentry-go/internal/protocol/log_batch.go
generated
vendored
Normal file
48
vendor/github.com/getsentry/sentry-go/internal/protocol/log_batch.go
generated
vendored
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
package protocol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/getsentry/sentry-go/internal/ratelimit"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LogAttribute is the JSON representation for a single log attribute value.
|
||||||
|
type LogAttribute struct {
|
||||||
|
Value any `json:"value"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logs is a container for multiple log items which knows how to convert
|
||||||
|
// itself into a single batched log envelope item.
|
||||||
|
type Logs []EnvelopeItemConvertible
|
||||||
|
|
||||||
|
func (ls Logs) ToEnvelopeItem() (*EnvelopeItem, error) {
|
||||||
|
// Convert each log to its JSON representation
|
||||||
|
items := make([]json.RawMessage, 0, len(ls))
|
||||||
|
for _, log := range ls {
|
||||||
|
envItem, err := log.ToEnvelopeItem()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
items = append(items, envItem.Payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(items) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapper := struct {
|
||||||
|
Items []json.RawMessage `json:"items"`
|
||||||
|
}{Items: items}
|
||||||
|
|
||||||
|
payload, err := json.Marshal(wrapper)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return NewLogItem(len(ls), payload), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Logs) GetCategory() ratelimit.Category { return ratelimit.CategoryLog }
|
||||||
|
func (Logs) GetEventID() string { return "" }
|
||||||
|
func (Logs) GetSdkInfo() *SdkInfo { return nil }
|
||||||
|
func (Logs) GetDynamicSamplingContext() map[string]string { return nil }
|
||||||
18
vendor/github.com/getsentry/sentry-go/internal/protocol/uuid.go
generated
vendored
Normal file
18
vendor/github.com/getsentry/sentry-go/internal/protocol/uuid.go
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
package protocol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/hex"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GenerateEventID generates a random UUID v4 for use as a Sentry event ID.
|
||||||
|
func GenerateEventID() string {
|
||||||
|
id := make([]byte, 16)
|
||||||
|
// Prefer rand.Read over rand.Reader, see https://go-review.googlesource.com/c/go/+/272326/.
|
||||||
|
_, _ = rand.Read(id)
|
||||||
|
id[6] &= 0x0F // clear version
|
||||||
|
id[6] |= 0x40 // set version to 4 (random uuid)
|
||||||
|
id[8] &= 0x3F // clear variant
|
||||||
|
id[8] |= 0x80 // set to IETF variant
|
||||||
|
return hex.EncodeToString(id)
|
||||||
|
}
|
||||||
4
vendor/github.com/getsentry/sentry-go/internal/ratelimit/category.go
generated
vendored
4
vendor/github.com/getsentry/sentry-go/internal/ratelimit/category.go
generated
vendored
|
|
@ -94,9 +94,9 @@ func (c Category) GetPriority() Priority {
|
||||||
case CategoryMonitor:
|
case CategoryMonitor:
|
||||||
return PriorityHigh
|
return PriorityHigh
|
||||||
case CategoryLog:
|
case CategoryLog:
|
||||||
return PriorityMedium
|
|
||||||
case CategoryTransaction:
|
|
||||||
return PriorityLow
|
return PriorityLow
|
||||||
|
case CategoryTransaction:
|
||||||
|
return PriorityMedium
|
||||||
default:
|
default:
|
||||||
return PriorityMedium
|
return PriorityMedium
|
||||||
}
|
}
|
||||||
|
|
|
||||||
398
vendor/github.com/getsentry/sentry-go/internal/telemetry/bucketed_buffer.go
generated
vendored
Normal file
398
vendor/github.com/getsentry/sentry-go/internal/telemetry/bucketed_buffer.go
generated
vendored
Normal file
|
|
@ -0,0 +1,398 @@
|
||||||
|
package telemetry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/getsentry/sentry-go/internal/ratelimit"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultBucketedCapacity = 100
|
||||||
|
perBucketItemLimit = 100
|
||||||
|
)
|
||||||
|
|
||||||
|
type Bucket[T any] struct {
|
||||||
|
traceID string
|
||||||
|
items []T
|
||||||
|
createdAt time.Time
|
||||||
|
lastUpdatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// BucketedBuffer groups items by trace id, flushing per bucket.
|
||||||
|
type BucketedBuffer[T any] struct {
|
||||||
|
mu sync.RWMutex
|
||||||
|
|
||||||
|
buckets []*Bucket[T]
|
||||||
|
traceIndex map[string]int
|
||||||
|
|
||||||
|
head int
|
||||||
|
tail int
|
||||||
|
|
||||||
|
itemCapacity int
|
||||||
|
bucketCapacity int
|
||||||
|
|
||||||
|
totalItems int
|
||||||
|
bucketCount int
|
||||||
|
|
||||||
|
category ratelimit.Category
|
||||||
|
priority ratelimit.Priority
|
||||||
|
overflowPolicy OverflowPolicy
|
||||||
|
batchSize int
|
||||||
|
timeout time.Duration
|
||||||
|
lastFlushTime time.Time
|
||||||
|
|
||||||
|
offered int64
|
||||||
|
dropped int64
|
||||||
|
onDropped func(item T, reason string)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBucketedBuffer[T any](
|
||||||
|
category ratelimit.Category,
|
||||||
|
capacity int,
|
||||||
|
overflowPolicy OverflowPolicy,
|
||||||
|
batchSize int,
|
||||||
|
timeout time.Duration,
|
||||||
|
) *BucketedBuffer[T] {
|
||||||
|
if capacity <= 0 {
|
||||||
|
capacity = defaultBucketedCapacity
|
||||||
|
}
|
||||||
|
if batchSize <= 0 {
|
||||||
|
batchSize = 1
|
||||||
|
}
|
||||||
|
if timeout < 0 {
|
||||||
|
timeout = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
bucketCapacity := capacity / 10
|
||||||
|
if bucketCapacity < 10 {
|
||||||
|
bucketCapacity = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
return &BucketedBuffer[T]{
|
||||||
|
buckets: make([]*Bucket[T], bucketCapacity),
|
||||||
|
traceIndex: make(map[string]int),
|
||||||
|
itemCapacity: capacity,
|
||||||
|
bucketCapacity: bucketCapacity,
|
||||||
|
category: category,
|
||||||
|
priority: category.GetPriority(),
|
||||||
|
overflowPolicy: overflowPolicy,
|
||||||
|
batchSize: batchSize,
|
||||||
|
timeout: timeout,
|
||||||
|
lastFlushTime: time.Now(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BucketedBuffer[T]) Offer(item T) bool {
|
||||||
|
atomic.AddInt64(&b.offered, 1)
|
||||||
|
|
||||||
|
traceID := ""
|
||||||
|
if ta, ok := any(item).(TraceAware); ok {
|
||||||
|
if tid, hasTrace := ta.GetTraceID(); hasTrace {
|
||||||
|
traceID = tid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
return b.offerToBucket(item, traceID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BucketedBuffer[T]) offerToBucket(item T, traceID string) bool {
|
||||||
|
if traceID != "" {
|
||||||
|
if idx, exists := b.traceIndex[traceID]; exists {
|
||||||
|
bucket := b.buckets[idx]
|
||||||
|
if len(bucket.items) >= perBucketItemLimit {
|
||||||
|
delete(b.traceIndex, traceID)
|
||||||
|
} else {
|
||||||
|
bucket.items = append(bucket.items, item)
|
||||||
|
bucket.lastUpdatedAt = time.Now()
|
||||||
|
b.totalItems++
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.totalItems >= b.itemCapacity {
|
||||||
|
return b.handleOverflow(item, traceID)
|
||||||
|
}
|
||||||
|
if b.bucketCount >= b.bucketCapacity {
|
||||||
|
return b.handleOverflow(item, traceID)
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket := &Bucket[T]{
|
||||||
|
traceID: traceID,
|
||||||
|
items: []T{item},
|
||||||
|
createdAt: time.Now(),
|
||||||
|
lastUpdatedAt: time.Now(),
|
||||||
|
}
|
||||||
|
b.buckets[b.tail] = bucket
|
||||||
|
if traceID != "" {
|
||||||
|
b.traceIndex[traceID] = b.tail
|
||||||
|
}
|
||||||
|
b.tail = (b.tail + 1) % b.bucketCapacity
|
||||||
|
b.bucketCount++
|
||||||
|
b.totalItems++
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BucketedBuffer[T]) handleOverflow(item T, traceID string) bool {
|
||||||
|
switch b.overflowPolicy {
|
||||||
|
case OverflowPolicyDropOldest:
|
||||||
|
oldestBucket := b.buckets[b.head]
|
||||||
|
if oldestBucket == nil {
|
||||||
|
atomic.AddInt64(&b.dropped, 1)
|
||||||
|
if b.onDropped != nil {
|
||||||
|
b.onDropped(item, "buffer_full_invalid_state")
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if oldestBucket.traceID != "" {
|
||||||
|
delete(b.traceIndex, oldestBucket.traceID)
|
||||||
|
}
|
||||||
|
droppedCount := len(oldestBucket.items)
|
||||||
|
atomic.AddInt64(&b.dropped, int64(droppedCount))
|
||||||
|
if b.onDropped != nil {
|
||||||
|
for _, di := range oldestBucket.items {
|
||||||
|
b.onDropped(di, "buffer_full_drop_oldest_bucket")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.totalItems -= droppedCount
|
||||||
|
b.bucketCount--
|
||||||
|
b.head = (b.head + 1) % b.bucketCapacity
|
||||||
|
// add new bucket
|
||||||
|
bucket := &Bucket[T]{traceID: traceID, items: []T{item}, createdAt: time.Now(), lastUpdatedAt: time.Now()}
|
||||||
|
b.buckets[b.tail] = bucket
|
||||||
|
if traceID != "" {
|
||||||
|
b.traceIndex[traceID] = b.tail
|
||||||
|
}
|
||||||
|
b.tail = (b.tail + 1) % b.bucketCapacity
|
||||||
|
b.bucketCount++
|
||||||
|
b.totalItems++
|
||||||
|
return true
|
||||||
|
case OverflowPolicyDropNewest:
|
||||||
|
atomic.AddInt64(&b.dropped, 1)
|
||||||
|
if b.onDropped != nil {
|
||||||
|
b.onDropped(item, "buffer_full_drop_newest")
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
atomic.AddInt64(&b.dropped, 1)
|
||||||
|
if b.onDropped != nil {
|
||||||
|
b.onDropped(item, "unknown_overflow_policy")
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BucketedBuffer[T]) Poll() (T, bool) {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
var zero T
|
||||||
|
if b.bucketCount == 0 {
|
||||||
|
return zero, false
|
||||||
|
}
|
||||||
|
bucket := b.buckets[b.head]
|
||||||
|
if bucket == nil || len(bucket.items) == 0 {
|
||||||
|
return zero, false
|
||||||
|
}
|
||||||
|
item := bucket.items[0]
|
||||||
|
bucket.items = bucket.items[1:]
|
||||||
|
b.totalItems--
|
||||||
|
if len(bucket.items) == 0 {
|
||||||
|
if bucket.traceID != "" {
|
||||||
|
delete(b.traceIndex, bucket.traceID)
|
||||||
|
}
|
||||||
|
b.buckets[b.head] = nil
|
||||||
|
b.head = (b.head + 1) % b.bucketCapacity
|
||||||
|
b.bucketCount--
|
||||||
|
}
|
||||||
|
return item, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BucketedBuffer[T]) PollBatch(maxItems int) []T {
|
||||||
|
if maxItems <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
if b.bucketCount == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
res := make([]T, 0, maxItems)
|
||||||
|
for len(res) < maxItems && b.bucketCount > 0 {
|
||||||
|
bucket := b.buckets[b.head]
|
||||||
|
if bucket == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
n := maxItems - len(res)
|
||||||
|
if n > len(bucket.items) {
|
||||||
|
n = len(bucket.items)
|
||||||
|
}
|
||||||
|
res = append(res, bucket.items[:n]...)
|
||||||
|
bucket.items = bucket.items[n:]
|
||||||
|
b.totalItems -= n
|
||||||
|
if len(bucket.items) == 0 {
|
||||||
|
if bucket.traceID != "" {
|
||||||
|
delete(b.traceIndex, bucket.traceID)
|
||||||
|
}
|
||||||
|
b.buckets[b.head] = nil
|
||||||
|
b.head = (b.head + 1) % b.bucketCapacity
|
||||||
|
b.bucketCount--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BucketedBuffer[T]) PollIfReady() []T {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
if b.bucketCount == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ready := b.totalItems >= b.batchSize || (b.timeout > 0 && time.Since(b.lastFlushTime) >= b.timeout)
|
||||||
|
if !ready {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
oldest := b.buckets[b.head]
|
||||||
|
if oldest == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
items := oldest.items
|
||||||
|
if oldest.traceID != "" {
|
||||||
|
delete(b.traceIndex, oldest.traceID)
|
||||||
|
}
|
||||||
|
b.buckets[b.head] = nil
|
||||||
|
b.head = (b.head + 1) % b.bucketCapacity
|
||||||
|
b.totalItems -= len(items)
|
||||||
|
b.bucketCount--
|
||||||
|
b.lastFlushTime = time.Now()
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BucketedBuffer[T]) Drain() []T {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
if b.bucketCount == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
res := make([]T, 0, b.totalItems)
|
||||||
|
for i := 0; i < b.bucketCount; i++ {
|
||||||
|
idx := (b.head + i) % b.bucketCapacity
|
||||||
|
bucket := b.buckets[idx]
|
||||||
|
if bucket != nil {
|
||||||
|
res = append(res, bucket.items...)
|
||||||
|
b.buckets[idx] = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.traceIndex = make(map[string]int)
|
||||||
|
b.head = 0
|
||||||
|
b.tail = 0
|
||||||
|
b.totalItems = 0
|
||||||
|
b.bucketCount = 0
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BucketedBuffer[T]) Peek() (T, bool) {
|
||||||
|
b.mu.RLock()
|
||||||
|
defer b.mu.RUnlock()
|
||||||
|
var zero T
|
||||||
|
if b.bucketCount == 0 {
|
||||||
|
return zero, false
|
||||||
|
}
|
||||||
|
bucket := b.buckets[b.head]
|
||||||
|
if bucket == nil || len(bucket.items) == 0 {
|
||||||
|
return zero, false
|
||||||
|
}
|
||||||
|
return bucket.items[0], true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BucketedBuffer[T]) Size() int { b.mu.RLock(); defer b.mu.RUnlock(); return b.totalItems }
|
||||||
|
func (b *BucketedBuffer[T]) Capacity() int { b.mu.RLock(); defer b.mu.RUnlock(); return b.itemCapacity }
|
||||||
|
func (b *BucketedBuffer[T]) Category() ratelimit.Category {
|
||||||
|
b.mu.RLock()
|
||||||
|
defer b.mu.RUnlock()
|
||||||
|
return b.category
|
||||||
|
}
|
||||||
|
func (b *BucketedBuffer[T]) Priority() ratelimit.Priority {
|
||||||
|
b.mu.RLock()
|
||||||
|
defer b.mu.RUnlock()
|
||||||
|
return b.priority
|
||||||
|
}
|
||||||
|
func (b *BucketedBuffer[T]) IsEmpty() bool {
|
||||||
|
b.mu.RLock()
|
||||||
|
defer b.mu.RUnlock()
|
||||||
|
return b.bucketCount == 0
|
||||||
|
}
|
||||||
|
func (b *BucketedBuffer[T]) IsFull() bool {
|
||||||
|
b.mu.RLock()
|
||||||
|
defer b.mu.RUnlock()
|
||||||
|
return b.totalItems >= b.itemCapacity
|
||||||
|
}
|
||||||
|
func (b *BucketedBuffer[T]) Utilization() float64 {
|
||||||
|
b.mu.RLock()
|
||||||
|
defer b.mu.RUnlock()
|
||||||
|
if b.itemCapacity == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return float64(b.totalItems) / float64(b.itemCapacity)
|
||||||
|
}
|
||||||
|
func (b *BucketedBuffer[T]) OfferedCount() int64 { return atomic.LoadInt64(&b.offered) }
|
||||||
|
func (b *BucketedBuffer[T]) DroppedCount() int64 { return atomic.LoadInt64(&b.dropped) }
|
||||||
|
func (b *BucketedBuffer[T]) AcceptedCount() int64 { return b.OfferedCount() - b.DroppedCount() }
|
||||||
|
func (b *BucketedBuffer[T]) DropRate() float64 {
|
||||||
|
off := b.OfferedCount()
|
||||||
|
if off == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return float64(b.DroppedCount()) / float64(off)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BucketedBuffer[T]) GetMetrics() BufferMetrics {
|
||||||
|
b.mu.RLock()
|
||||||
|
size := b.totalItems
|
||||||
|
util := 0.0
|
||||||
|
if b.itemCapacity > 0 {
|
||||||
|
util = float64(b.totalItems) / float64(b.itemCapacity)
|
||||||
|
}
|
||||||
|
b.mu.RUnlock()
|
||||||
|
return BufferMetrics{Category: b.category, Priority: b.priority, Capacity: b.itemCapacity, Size: size, Utilization: util, OfferedCount: b.OfferedCount(), DroppedCount: b.DroppedCount(), AcceptedCount: b.AcceptedCount(), DropRate: b.DropRate(), LastUpdated: time.Now()}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BucketedBuffer[T]) SetDroppedCallback(callback func(item T, reason string)) {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
b.onDropped = callback
|
||||||
|
}
|
||||||
|
func (b *BucketedBuffer[T]) Clear() {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
for i := 0; i < b.bucketCapacity; i++ {
|
||||||
|
b.buckets[i] = nil
|
||||||
|
}
|
||||||
|
b.traceIndex = make(map[string]int)
|
||||||
|
b.head = 0
|
||||||
|
b.tail = 0
|
||||||
|
b.totalItems = 0
|
||||||
|
b.bucketCount = 0
|
||||||
|
}
|
||||||
|
func (b *BucketedBuffer[T]) IsReadyToFlush() bool {
|
||||||
|
b.mu.RLock()
|
||||||
|
defer b.mu.RUnlock()
|
||||||
|
if b.bucketCount == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if b.totalItems >= b.batchSize {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if b.timeout > 0 && time.Since(b.lastFlushTime) >= b.timeout {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
func (b *BucketedBuffer[T]) MarkFlushed() {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
b.lastFlushTime = time.Now()
|
||||||
|
}
|
||||||
378
vendor/github.com/getsentry/sentry-go/internal/telemetry/buffer.go
generated
vendored
Normal file
378
vendor/github.com/getsentry/sentry-go/internal/telemetry/buffer.go
generated
vendored
Normal file
|
|
@ -0,0 +1,378 @@
|
||||||
|
package telemetry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/getsentry/sentry-go/internal/ratelimit"
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultCapacity = 100
|
||||||
|
|
||||||
|
// RingBuffer is a thread-safe ring buffer with overflow policies.
|
||||||
|
type RingBuffer[T any] struct {
|
||||||
|
mu sync.RWMutex
|
||||||
|
items []T
|
||||||
|
head int
|
||||||
|
tail int
|
||||||
|
size int
|
||||||
|
capacity int
|
||||||
|
|
||||||
|
category ratelimit.Category
|
||||||
|
priority ratelimit.Priority
|
||||||
|
overflowPolicy OverflowPolicy
|
||||||
|
|
||||||
|
batchSize int
|
||||||
|
timeout time.Duration
|
||||||
|
lastFlushTime time.Time
|
||||||
|
|
||||||
|
offered int64
|
||||||
|
dropped int64
|
||||||
|
onDropped func(item T, reason string)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRingBuffer[T any](category ratelimit.Category, capacity int, overflowPolicy OverflowPolicy, batchSize int, timeout time.Duration) *RingBuffer[T] {
|
||||||
|
if capacity <= 0 {
|
||||||
|
capacity = defaultCapacity
|
||||||
|
}
|
||||||
|
|
||||||
|
if batchSize <= 0 {
|
||||||
|
batchSize = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if timeout < 0 {
|
||||||
|
timeout = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return &RingBuffer[T]{
|
||||||
|
items: make([]T, capacity),
|
||||||
|
capacity: capacity,
|
||||||
|
category: category,
|
||||||
|
priority: category.GetPriority(),
|
||||||
|
overflowPolicy: overflowPolicy,
|
||||||
|
batchSize: batchSize,
|
||||||
|
timeout: timeout,
|
||||||
|
lastFlushTime: time.Now(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *RingBuffer[T]) SetDroppedCallback(callback func(item T, reason string)) {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
b.onDropped = callback
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *RingBuffer[T]) Offer(item T) bool {
|
||||||
|
atomic.AddInt64(&b.offered, 1)
|
||||||
|
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
|
if b.size < b.capacity {
|
||||||
|
b.items[b.tail] = item
|
||||||
|
b.tail = (b.tail + 1) % b.capacity
|
||||||
|
b.size++
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch b.overflowPolicy {
|
||||||
|
case OverflowPolicyDropOldest:
|
||||||
|
oldItem := b.items[b.head]
|
||||||
|
b.items[b.head] = item
|
||||||
|
b.head = (b.head + 1) % b.capacity
|
||||||
|
b.tail = (b.tail + 1) % b.capacity
|
||||||
|
|
||||||
|
atomic.AddInt64(&b.dropped, 1)
|
||||||
|
if b.onDropped != nil {
|
||||||
|
b.onDropped(oldItem, "buffer_full_drop_oldest")
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
|
||||||
|
case OverflowPolicyDropNewest:
|
||||||
|
atomic.AddInt64(&b.dropped, 1)
|
||||||
|
if b.onDropped != nil {
|
||||||
|
b.onDropped(item, "buffer_full_drop_newest")
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
|
||||||
|
default:
|
||||||
|
atomic.AddInt64(&b.dropped, 1)
|
||||||
|
if b.onDropped != nil {
|
||||||
|
b.onDropped(item, "unknown_overflow_policy")
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *RingBuffer[T]) Poll() (T, bool) {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
|
var zero T
|
||||||
|
if b.size == 0 {
|
||||||
|
return zero, false
|
||||||
|
}
|
||||||
|
|
||||||
|
item := b.items[b.head]
|
||||||
|
b.items[b.head] = zero
|
||||||
|
b.head = (b.head + 1) % b.capacity
|
||||||
|
b.size--
|
||||||
|
|
||||||
|
return item, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *RingBuffer[T]) PollBatch(maxItems int) []T {
|
||||||
|
if maxItems <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
|
if b.size == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
itemCount := maxItems
|
||||||
|
if itemCount > b.size {
|
||||||
|
itemCount = b.size
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]T, itemCount)
|
||||||
|
var zero T
|
||||||
|
|
||||||
|
for i := 0; i < itemCount; i++ {
|
||||||
|
result[i] = b.items[b.head]
|
||||||
|
b.items[b.head] = zero
|
||||||
|
b.head = (b.head + 1) % b.capacity
|
||||||
|
b.size--
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *RingBuffer[T]) Drain() []T {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
|
if b.size == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]T, b.size)
|
||||||
|
index := 0
|
||||||
|
var zero T
|
||||||
|
|
||||||
|
for i := 0; i < b.size; i++ {
|
||||||
|
pos := (b.head + i) % b.capacity
|
||||||
|
result[index] = b.items[pos]
|
||||||
|
b.items[pos] = zero
|
||||||
|
index++
|
||||||
|
}
|
||||||
|
|
||||||
|
b.head = 0
|
||||||
|
b.tail = 0
|
||||||
|
b.size = 0
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *RingBuffer[T]) Peek() (T, bool) {
|
||||||
|
b.mu.RLock()
|
||||||
|
defer b.mu.RUnlock()
|
||||||
|
|
||||||
|
var zero T
|
||||||
|
if b.size == 0 {
|
||||||
|
return zero, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.items[b.head], true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *RingBuffer[T]) Size() int {
|
||||||
|
b.mu.RLock()
|
||||||
|
defer b.mu.RUnlock()
|
||||||
|
return b.size
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *RingBuffer[T]) Capacity() int {
|
||||||
|
b.mu.RLock()
|
||||||
|
defer b.mu.RUnlock()
|
||||||
|
return b.capacity
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *RingBuffer[T]) Category() ratelimit.Category {
|
||||||
|
b.mu.RLock()
|
||||||
|
defer b.mu.RUnlock()
|
||||||
|
return b.category
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *RingBuffer[T]) Priority() ratelimit.Priority {
|
||||||
|
b.mu.RLock()
|
||||||
|
defer b.mu.RUnlock()
|
||||||
|
return b.priority
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *RingBuffer[T]) IsEmpty() bool {
|
||||||
|
b.mu.RLock()
|
||||||
|
defer b.mu.RUnlock()
|
||||||
|
return b.size == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *RingBuffer[T]) IsFull() bool {
|
||||||
|
b.mu.RLock()
|
||||||
|
defer b.mu.RUnlock()
|
||||||
|
return b.size == b.capacity
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *RingBuffer[T]) Utilization() float64 {
|
||||||
|
b.mu.RLock()
|
||||||
|
defer b.mu.RUnlock()
|
||||||
|
return float64(b.size) / float64(b.capacity)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *RingBuffer[T]) OfferedCount() int64 {
|
||||||
|
return atomic.LoadInt64(&b.offered)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *RingBuffer[T]) DroppedCount() int64 {
|
||||||
|
return atomic.LoadInt64(&b.dropped)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *RingBuffer[T]) AcceptedCount() int64 {
|
||||||
|
return b.OfferedCount() - b.DroppedCount()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *RingBuffer[T]) DropRate() float64 {
|
||||||
|
offered := b.OfferedCount()
|
||||||
|
if offered == 0 {
|
||||||
|
return 0.0
|
||||||
|
}
|
||||||
|
return float64(b.DroppedCount()) / float64(offered)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *RingBuffer[T]) Clear() {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
|
var zero T
|
||||||
|
for i := 0; i < b.capacity; i++ {
|
||||||
|
b.items[i] = zero
|
||||||
|
}
|
||||||
|
|
||||||
|
b.head = 0
|
||||||
|
b.tail = 0
|
||||||
|
b.size = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *RingBuffer[T]) GetMetrics() BufferMetrics {
|
||||||
|
b.mu.RLock()
|
||||||
|
size := b.size
|
||||||
|
util := float64(b.size) / float64(b.capacity)
|
||||||
|
b.mu.RUnlock()
|
||||||
|
|
||||||
|
return BufferMetrics{
|
||||||
|
Category: b.category,
|
||||||
|
Priority: b.priority,
|
||||||
|
Capacity: b.capacity,
|
||||||
|
Size: size,
|
||||||
|
Utilization: util,
|
||||||
|
OfferedCount: b.OfferedCount(),
|
||||||
|
DroppedCount: b.DroppedCount(),
|
||||||
|
AcceptedCount: b.AcceptedCount(),
|
||||||
|
DropRate: b.DropRate(),
|
||||||
|
LastUpdated: time.Now(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *RingBuffer[T]) IsReadyToFlush() bool {
|
||||||
|
b.mu.RLock()
|
||||||
|
defer b.mu.RUnlock()
|
||||||
|
|
||||||
|
if b.size == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.size >= b.batchSize {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.timeout > 0 && time.Since(b.lastFlushTime) >= b.timeout {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *RingBuffer[T]) MarkFlushed() {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
b.lastFlushTime = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *RingBuffer[T]) PollIfReady() []T {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
|
if b.size == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ready := b.size >= b.batchSize ||
|
||||||
|
(b.timeout > 0 && time.Since(b.lastFlushTime) >= b.timeout)
|
||||||
|
|
||||||
|
if !ready {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
itemCount := b.batchSize
|
||||||
|
if itemCount > b.size {
|
||||||
|
itemCount = b.size
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]T, itemCount)
|
||||||
|
var zero T
|
||||||
|
|
||||||
|
for i := 0; i < itemCount; i++ {
|
||||||
|
result[i] = b.items[b.head]
|
||||||
|
b.items[b.head] = zero
|
||||||
|
b.head = (b.head + 1) % b.capacity
|
||||||
|
b.size--
|
||||||
|
}
|
||||||
|
|
||||||
|
b.lastFlushTime = time.Now()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
type BufferMetrics struct {
|
||||||
|
Category ratelimit.Category `json:"category"`
|
||||||
|
Priority ratelimit.Priority `json:"priority"`
|
||||||
|
Capacity int `json:"capacity"`
|
||||||
|
Size int `json:"size"`
|
||||||
|
Utilization float64 `json:"utilization"`
|
||||||
|
OfferedCount int64 `json:"offered_count"`
|
||||||
|
DroppedCount int64 `json:"dropped_count"`
|
||||||
|
AcceptedCount int64 `json:"accepted_count"`
|
||||||
|
DropRate float64 `json:"drop_rate"`
|
||||||
|
LastUpdated time.Time `json:"last_updated"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OverflowPolicy defines how the ring buffer handles overflow.
|
||||||
|
type OverflowPolicy int
|
||||||
|
|
||||||
|
const (
|
||||||
|
OverflowPolicyDropOldest OverflowPolicy = iota
|
||||||
|
OverflowPolicyDropNewest
|
||||||
|
)
|
||||||
|
|
||||||
|
func (op OverflowPolicy) String() string {
|
||||||
|
switch op {
|
||||||
|
case OverflowPolicyDropOldest:
|
||||||
|
return "drop_oldest"
|
||||||
|
case OverflowPolicyDropNewest:
|
||||||
|
return "drop_newest"
|
||||||
|
default:
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
49
vendor/github.com/getsentry/sentry-go/internal/telemetry/buffer_wrapper.go
generated
vendored
Normal file
49
vendor/github.com/getsentry/sentry-go/internal/telemetry/buffer_wrapper.go
generated
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
package telemetry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/getsentry/sentry-go/internal/protocol"
|
||||||
|
"github.com/getsentry/sentry-go/internal/ratelimit"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Buffer is the top-level buffer that wraps the scheduler and category buffers.
|
||||||
|
type Buffer struct {
|
||||||
|
scheduler *Scheduler
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBuffer creates a new Buffer with the given configuration.
|
||||||
|
func NewBuffer(
|
||||||
|
storage map[ratelimit.Category]Storage[protocol.EnvelopeItemConvertible],
|
||||||
|
transport protocol.TelemetryTransport,
|
||||||
|
dsn *protocol.Dsn,
|
||||||
|
sdkInfo *protocol.SdkInfo,
|
||||||
|
) *Buffer {
|
||||||
|
scheduler := NewScheduler(storage, transport, dsn, sdkInfo)
|
||||||
|
scheduler.Start()
|
||||||
|
|
||||||
|
return &Buffer{
|
||||||
|
scheduler: scheduler,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds an EnvelopeItemConvertible to the appropriate buffer based on its category.
|
||||||
|
func (b *Buffer) Add(item protocol.EnvelopeItemConvertible) bool {
|
||||||
|
return b.scheduler.Add(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush forces all buffers to flush within the given timeout.
|
||||||
|
func (b *Buffer) Flush(timeout time.Duration) bool {
|
||||||
|
return b.scheduler.Flush(timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlushWithContext flushes with a custom context for cancellation.
|
||||||
|
func (b *Buffer) FlushWithContext(ctx context.Context) bool {
|
||||||
|
return b.scheduler.FlushWithContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close stops the buffer, flushes remaining data, and releases resources.
|
||||||
|
func (b *Buffer) Close(timeout time.Duration) {
|
||||||
|
b.scheduler.Stop(timeout)
|
||||||
|
}
|
||||||
279
vendor/github.com/getsentry/sentry-go/internal/telemetry/scheduler.go
generated
vendored
Normal file
279
vendor/github.com/getsentry/sentry-go/internal/telemetry/scheduler.go
generated
vendored
Normal file
|
|
@ -0,0 +1,279 @@
|
||||||
|
package telemetry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/getsentry/sentry-go/internal/debuglog"
|
||||||
|
"github.com/getsentry/sentry-go/internal/protocol"
|
||||||
|
"github.com/getsentry/sentry-go/internal/ratelimit"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Scheduler implements a weighted round-robin scheduler for processing buffered events.
|
||||||
|
type Scheduler struct {
|
||||||
|
buffers map[ratelimit.Category]Storage[protocol.EnvelopeItemConvertible]
|
||||||
|
transport protocol.TelemetryTransport
|
||||||
|
dsn *protocol.Dsn
|
||||||
|
sdkInfo *protocol.SdkInfo
|
||||||
|
|
||||||
|
currentCycle []ratelimit.Priority
|
||||||
|
cyclePos int
|
||||||
|
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
|
processingWg sync.WaitGroup
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
|
cond *sync.Cond
|
||||||
|
startOnce sync.Once
|
||||||
|
finishOnce sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewScheduler(
|
||||||
|
buffers map[ratelimit.Category]Storage[protocol.EnvelopeItemConvertible],
|
||||||
|
transport protocol.TelemetryTransport,
|
||||||
|
dsn *protocol.Dsn,
|
||||||
|
sdkInfo *protocol.SdkInfo,
|
||||||
|
) *Scheduler {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
priorityWeights := map[ratelimit.Priority]int{
|
||||||
|
ratelimit.PriorityCritical: 5,
|
||||||
|
ratelimit.PriorityHigh: 4,
|
||||||
|
ratelimit.PriorityMedium: 3,
|
||||||
|
ratelimit.PriorityLow: 2,
|
||||||
|
ratelimit.PriorityLowest: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentCycle []ratelimit.Priority
|
||||||
|
for priority, weight := range priorityWeights {
|
||||||
|
hasBuffers := false
|
||||||
|
for _, buffer := range buffers {
|
||||||
|
if buffer.Priority() == priority {
|
||||||
|
hasBuffers = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasBuffers {
|
||||||
|
for i := 0; i < weight; i++ {
|
||||||
|
currentCycle = append(currentCycle, priority)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s := &Scheduler{
|
||||||
|
buffers: buffers,
|
||||||
|
transport: transport,
|
||||||
|
dsn: dsn,
|
||||||
|
sdkInfo: sdkInfo,
|
||||||
|
currentCycle: currentCycle,
|
||||||
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
|
}
|
||||||
|
s.cond = sync.NewCond(&s.mu)
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scheduler) Start() {
|
||||||
|
s.startOnce.Do(func() {
|
||||||
|
s.processingWg.Add(1)
|
||||||
|
go s.run()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scheduler) Stop(timeout time.Duration) {
|
||||||
|
s.finishOnce.Do(func() {
|
||||||
|
s.Flush(timeout)
|
||||||
|
|
||||||
|
s.cancel()
|
||||||
|
s.cond.Broadcast()
|
||||||
|
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
defer close(done)
|
||||||
|
s.processingWg.Wait()
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
case <-time.After(timeout):
|
||||||
|
debuglog.Printf("scheduler stop timed out after %v", timeout)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scheduler) Signal() {
|
||||||
|
s.cond.Signal()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scheduler) Add(item protocol.EnvelopeItemConvertible) bool {
|
||||||
|
category := item.GetCategory()
|
||||||
|
buffer, exists := s.buffers[category]
|
||||||
|
if !exists {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
accepted := buffer.Offer(item)
|
||||||
|
if accepted {
|
||||||
|
s.Signal()
|
||||||
|
}
|
||||||
|
return accepted
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scheduler) Flush(timeout time.Duration) bool {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||||
|
defer cancel()
|
||||||
|
return s.FlushWithContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scheduler) FlushWithContext(ctx context.Context) bool {
|
||||||
|
s.flushBuffers()
|
||||||
|
return s.transport.FlushWithContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scheduler) run() {
|
||||||
|
defer s.processingWg.Done()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
ticker := time.NewTicker(100 * time.Millisecond)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ticker.C:
|
||||||
|
s.cond.Broadcast()
|
||||||
|
case <-s.ctx.Done():
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
for {
|
||||||
|
s.mu.Lock()
|
||||||
|
|
||||||
|
for !s.hasWork() && s.ctx.Err() == nil {
|
||||||
|
s.cond.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.ctx.Err() != nil {
|
||||||
|
s.mu.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.mu.Unlock()
|
||||||
|
s.processNextBatch()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scheduler) hasWork() bool {
|
||||||
|
for _, buffer := range s.buffers {
|
||||||
|
if buffer.IsReadyToFlush() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scheduler) processNextBatch() {
|
||||||
|
if len(s.currentCycle) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
priority := s.currentCycle[s.cyclePos]
|
||||||
|
s.cyclePos = (s.cyclePos + 1) % len(s.currentCycle)
|
||||||
|
|
||||||
|
var bufferToProcess Storage[protocol.EnvelopeItemConvertible]
|
||||||
|
var categoryToProcess ratelimit.Category
|
||||||
|
for category, buffer := range s.buffers {
|
||||||
|
if buffer.Priority() == priority && buffer.IsReadyToFlush() {
|
||||||
|
bufferToProcess = buffer
|
||||||
|
categoryToProcess = category
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if bufferToProcess != nil {
|
||||||
|
s.processItems(bufferToProcess, categoryToProcess, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scheduler) processItems(buffer Storage[protocol.EnvelopeItemConvertible], category ratelimit.Category, force bool) {
|
||||||
|
var items []protocol.EnvelopeItemConvertible
|
||||||
|
|
||||||
|
if force {
|
||||||
|
items = buffer.Drain()
|
||||||
|
} else {
|
||||||
|
items = buffer.PollIfReady()
|
||||||
|
}
|
||||||
|
|
||||||
|
// drop the current batch if rate-limited or if transport is full
|
||||||
|
if len(items) == 0 || s.isRateLimited(category) || !s.transport.HasCapacity() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch category {
|
||||||
|
case ratelimit.CategoryLog:
|
||||||
|
logs := protocol.Logs(items)
|
||||||
|
header := &protocol.EnvelopeHeader{EventID: protocol.GenerateEventID(), SentAt: time.Now(), Sdk: s.sdkInfo}
|
||||||
|
if s.dsn != nil {
|
||||||
|
header.Dsn = s.dsn.String()
|
||||||
|
}
|
||||||
|
envelope := protocol.NewEnvelope(header)
|
||||||
|
item, err := logs.ToEnvelopeItem()
|
||||||
|
if err != nil {
|
||||||
|
debuglog.Printf("error creating log batch envelope item: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
envelope.AddItem(item)
|
||||||
|
if err := s.transport.SendEnvelope(envelope); err != nil {
|
||||||
|
debuglog.Printf("error sending envelope: %v", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
// if the buffers are properly configured, buffer.PollIfReady should return a single item for every category
|
||||||
|
// other than logs. We still iterate over the items just in case, because we don't want to send broken envelopes.
|
||||||
|
for _, it := range items {
|
||||||
|
s.sendItem(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scheduler) sendItem(item protocol.EnvelopeItemConvertible) {
|
||||||
|
header := &protocol.EnvelopeHeader{
|
||||||
|
EventID: item.GetEventID(),
|
||||||
|
SentAt: time.Now(),
|
||||||
|
Trace: item.GetDynamicSamplingContext(),
|
||||||
|
Sdk: s.sdkInfo,
|
||||||
|
}
|
||||||
|
if header.EventID == "" {
|
||||||
|
header.EventID = protocol.GenerateEventID()
|
||||||
|
}
|
||||||
|
if s.dsn != nil {
|
||||||
|
header.Dsn = s.dsn.String()
|
||||||
|
}
|
||||||
|
envelope := protocol.NewEnvelope(header)
|
||||||
|
envItem, err := item.ToEnvelopeItem()
|
||||||
|
if err != nil {
|
||||||
|
debuglog.Printf("error while converting to envelope item: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
envelope.AddItem(envItem)
|
||||||
|
if err := s.transport.SendEnvelope(envelope); err != nil {
|
||||||
|
debuglog.Printf("error sending envelope: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scheduler) flushBuffers() {
|
||||||
|
for category, buffer := range s.buffers {
|
||||||
|
if !buffer.IsEmpty() {
|
||||||
|
s.processItems(buffer, category, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scheduler) isRateLimited(category ratelimit.Category) bool {
|
||||||
|
return s.transport.IsRateLimited(category)
|
||||||
|
}
|
||||||
42
vendor/github.com/getsentry/sentry-go/internal/telemetry/storage.go
generated
vendored
Normal file
42
vendor/github.com/getsentry/sentry-go/internal/telemetry/storage.go
generated
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
package telemetry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/getsentry/sentry-go/internal/ratelimit"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Storage defines the common interface for all buffer storage implementations.
|
||||||
|
type Storage[T any] interface {
|
||||||
|
// Core operations
|
||||||
|
Offer(item T) bool
|
||||||
|
Poll() (T, bool)
|
||||||
|
PollBatch(maxItems int) []T
|
||||||
|
PollIfReady() []T
|
||||||
|
Drain() []T
|
||||||
|
Peek() (T, bool)
|
||||||
|
|
||||||
|
// State queries
|
||||||
|
Size() int
|
||||||
|
Capacity() int
|
||||||
|
IsEmpty() bool
|
||||||
|
IsFull() bool
|
||||||
|
Utilization() float64
|
||||||
|
|
||||||
|
// Flush management
|
||||||
|
IsReadyToFlush() bool
|
||||||
|
MarkFlushed()
|
||||||
|
|
||||||
|
// Category/Priority
|
||||||
|
Category() ratelimit.Category
|
||||||
|
Priority() ratelimit.Priority
|
||||||
|
|
||||||
|
// Metrics
|
||||||
|
OfferedCount() int64
|
||||||
|
DroppedCount() int64
|
||||||
|
AcceptedCount() int64
|
||||||
|
DropRate() float64
|
||||||
|
GetMetrics() BufferMetrics
|
||||||
|
|
||||||
|
// Configuration
|
||||||
|
SetDroppedCallback(callback func(item T, reason string))
|
||||||
|
Clear()
|
||||||
|
}
|
||||||
7
vendor/github.com/getsentry/sentry-go/internal/telemetry/trace_aware.go
generated
vendored
Normal file
7
vendor/github.com/getsentry/sentry-go/internal/telemetry/trace_aware.go
generated
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
package telemetry
|
||||||
|
|
||||||
|
// TraceAware is implemented by items that can expose a trace ID.
|
||||||
|
// BucketedBuffer uses this to group items by trace.
|
||||||
|
type TraceAware interface {
|
||||||
|
GetTraceID() (string, bool)
|
||||||
|
}
|
||||||
17
vendor/github.com/getsentry/sentry-go/log.go
generated
vendored
17
vendor/github.com/getsentry/sentry-go/log.go
generated
vendored
|
|
@ -66,7 +66,7 @@ func NewLogger(ctx context.Context) Logger {
|
||||||
}
|
}
|
||||||
|
|
||||||
client := hub.Client()
|
client := hub.Client()
|
||||||
if client != nil && client.batchLogger != nil {
|
if client != nil && client.options.EnableLogs {
|
||||||
return &sentryLogger{
|
return &sentryLogger{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
client: client,
|
client: client,
|
||||||
|
|
@ -76,11 +76,10 @@ func NewLogger(ctx context.Context) Logger {
|
||||||
}
|
}
|
||||||
|
|
||||||
debuglog.Println("fallback to noopLogger: enableLogs disabled")
|
debuglog.Println("fallback to noopLogger: enableLogs disabled")
|
||||||
return &noopLogger{} // fallback: does nothing
|
return &noopLogger{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *sentryLogger) Write(p []byte) (int, error) {
|
func (l *sentryLogger) Write(p []byte) (int, error) {
|
||||||
// Avoid sending double newlines to Sentry
|
|
||||||
msg := strings.TrimRight(string(p), "\n")
|
msg := strings.TrimRight(string(p), "\n")
|
||||||
l.Info().Emit(msg)
|
l.Info().Emit(msg)
|
||||||
return len(p), nil
|
return len(p), nil
|
||||||
|
|
@ -145,8 +144,6 @@ func (l *sentryLogger) log(ctx context.Context, level LogLevel, severity int, me
|
||||||
for k, v := range entryAttrs {
|
for k, v := range entryAttrs {
|
||||||
attrs[k] = v
|
attrs[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set default attributes
|
|
||||||
if release := l.client.options.Release; release != "" {
|
if release := l.client.options.Release; release != "" {
|
||||||
attrs["sentry.release"] = Attribute{Value: release, Type: AttributeString}
|
attrs["sentry.release"] = Attribute{Value: release, Type: AttributeString}
|
||||||
}
|
}
|
||||||
|
|
@ -194,7 +191,13 @@ func (l *sentryLogger) log(ctx context.Context, level LogLevel, severity int, me
|
||||||
}
|
}
|
||||||
|
|
||||||
if log != nil {
|
if log != nil {
|
||||||
l.client.batchLogger.logCh <- *log
|
if l.client.telemetryBuffer != nil {
|
||||||
|
if !l.client.telemetryBuffer.Add(log) {
|
||||||
|
debuglog.Print("Dropping event: log buffer full or category missing")
|
||||||
|
}
|
||||||
|
} else if l.client.batchLogger != nil {
|
||||||
|
l.client.batchLogger.logCh <- *log
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if l.client.options.Debug {
|
if l.client.options.Debug {
|
||||||
|
|
@ -287,7 +290,7 @@ func (l *sentryLogger) Panic() LogEntry {
|
||||||
level: LogLevelFatal,
|
level: LogLevelFatal,
|
||||||
severity: LogSeverityFatal,
|
severity: LogSeverityFatal,
|
||||||
attributes: make(map[string]Attribute),
|
attributes: make(map[string]Attribute),
|
||||||
shouldPanic: true, // this should panic instead of exit
|
shouldPanic: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
2
vendor/github.com/getsentry/sentry-go/sentry.go
generated
vendored
2
vendor/github.com/getsentry/sentry-go/sentry.go
generated
vendored
|
|
@ -6,7 +6,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// The version of the SDK.
|
// The version of the SDK.
|
||||||
const SDKVersion = "0.36.2"
|
const SDKVersion = "0.40.0"
|
||||||
|
|
||||||
// apiVersion is the minimum version of the Sentry API compatible with the
|
// apiVersion is the minimum version of the Sentry API compatible with the
|
||||||
// sentry-go SDK.
|
// sentry-go SDK.
|
||||||
|
|
|
||||||
24
vendor/github.com/getsentry/sentry-go/transport.go
generated
vendored
24
vendor/github.com/getsentry/sentry-go/transport.go
generated
vendored
|
|
@ -788,7 +788,29 @@ func (a *internalAsyncTransportAdapter) Configure(options ClientOptions) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *internalAsyncTransportAdapter) SendEvent(event *Event) {
|
func (a *internalAsyncTransportAdapter) SendEvent(event *Event) {
|
||||||
a.transport.SendEvent(event)
|
header := &protocol.EnvelopeHeader{EventID: string(event.EventID), SentAt: time.Now(), Sdk: &protocol.SdkInfo{Name: event.Sdk.Name, Version: event.Sdk.Version}}
|
||||||
|
if a.dsn != nil {
|
||||||
|
header.Dsn = a.dsn.String()
|
||||||
|
}
|
||||||
|
if header.EventID == "" {
|
||||||
|
header.EventID = protocol.GenerateEventID()
|
||||||
|
}
|
||||||
|
envelope := protocol.NewEnvelope(header)
|
||||||
|
item, err := event.ToEnvelopeItem()
|
||||||
|
if err != nil {
|
||||||
|
debuglog.Printf("Failed to convert event to envelope item: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
envelope.AddItem(item)
|
||||||
|
|
||||||
|
for _, attachment := range event.Attachments {
|
||||||
|
attachmentItem := protocol.NewAttachmentItem(attachment.Filename, attachment.ContentType, attachment.Payload)
|
||||||
|
envelope.AddItem(attachmentItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := a.transport.SendEnvelope(envelope); err != nil {
|
||||||
|
debuglog.Printf("Error sending envelope: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *internalAsyncTransportAdapter) Flush(timeout time.Duration) bool {
|
func (a *internalAsyncTransportAdapter) Flush(timeout time.Duration) bool {
|
||||||
|
|
|
||||||
12
vendor/github.com/getsentry/sentry-go/util.go
generated
vendored
12
vendor/github.com/getsentry/sentry-go/util.go
generated
vendored
|
|
@ -1,8 +1,6 @@
|
||||||
package sentry
|
package sentry
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -11,18 +9,12 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/getsentry/sentry-go/internal/debuglog"
|
"github.com/getsentry/sentry-go/internal/debuglog"
|
||||||
|
"github.com/getsentry/sentry-go/internal/protocol"
|
||||||
exec "golang.org/x/sys/execabs"
|
exec "golang.org/x/sys/execabs"
|
||||||
)
|
)
|
||||||
|
|
||||||
func uuid() string {
|
func uuid() string {
|
||||||
id := make([]byte, 16)
|
return protocol.GenerateEventID()
|
||||||
// Prefer rand.Read over rand.Reader, see https://go-review.googlesource.com/c/go/+/272326/.
|
|
||||||
_, _ = rand.Read(id)
|
|
||||||
id[6] &= 0x0F // clear version
|
|
||||||
id[6] |= 0x40 // set version to 4 (random uuid)
|
|
||||||
id[8] &= 0x3F // clear variant
|
|
||||||
id[8] |= 0x80 // set to IETF variant
|
|
||||||
return hex.EncodeToString(id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fileExists(fileName string) bool {
|
func fileExists(fileName string) bool {
|
||||||
|
|
|
||||||
7
vendor/github.com/olekukonko/ll/global.go
generated
vendored
7
vendor/github.com/olekukonko/ll/global.go
generated
vendored
|
|
@ -657,6 +657,13 @@ func Mark(names ...string) {
|
||||||
// It is similar to Dbg but formats the output as JSON for better readability. It is thread-safe and respects
|
// 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).
|
// the logger’s configuration (e.g., enabled, level, suspend, handler, middleware).
|
||||||
func Output(values ...interface{}) {
|
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 := NewInspector(defaultLogger)
|
||||||
o.Log(2, values...)
|
o.Log(2, values...)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
vendor/github.com/olekukonko/ll/inspector.go
generated
vendored
2
vendor/github.com/olekukonko/ll/inspector.go
generated
vendored
|
|
@ -79,7 +79,7 @@ func (o *Inspector) Log(skip int, values ...interface{}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct log message with file, line, and JSON data
|
// Construct log message with file, line, and JSON data
|
||||||
msg := fmt.Sprintf("[%s:%d] DUMP: %s", shortFile, line, string(jsonData))
|
msg := fmt.Sprintf("[%s:%d] INSPECT: %s", shortFile, line, string(jsonData))
|
||||||
o.logger.log(lx.LevelInfo, lx.ClassText, msg, nil, false)
|
o.logger.log(lx.LevelInfo, lx.ClassText, msg, nil, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
61
vendor/github.com/olekukonko/ll/ll.go
generated
vendored
61
vendor/github.com/olekukonko/ll/ll.go
generated
vendored
|
|
@ -350,17 +350,58 @@ func (l *Logger) Dump(values ...interface{}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output logs data in a human-readable JSON format at Info level, including caller file and line information.
|
// Output logs each value as pretty-printed JSON for REST debugging.
|
||||||
// It is similar to Dbg but formats the output as JSON for better readability. It is thread-safe and respects
|
// Each value is logged on its own line with [file:line] and a blank line after the header.
|
||||||
// the logger's configuration (e.g., enabled, level, suspend, handler, middleware).
|
// Ideal for inspecting outgoing/incoming REST payloads.
|
||||||
// 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{}) {
|
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 := NewInspector(l)
|
||||||
o.Log(2, values...)
|
o.Log(2, values...)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1
vendor/github.com/olekukonko/tablewriter/.gitignore
generated
vendored
1
vendor/github.com/olekukonko/tablewriter/.gitignore
generated
vendored
|
|
@ -8,3 +8,4 @@
|
||||||
dev.sh
|
dev.sh
|
||||||
*csv2table
|
*csv2table
|
||||||
_test/
|
_test/
|
||||||
|
*.test
|
||||||
|
|
|
||||||
96
vendor/github.com/olekukonko/tablewriter/MIGRATION.md
generated
vendored
96
vendor/github.com/olekukonko/tablewriter/MIGRATION.md
generated
vendored
|
|
@ -206,51 +206,51 @@ func main() {
|
||||||
|
|
||||||
The `defaultConfig()` function (`config.go:defaultConfig`) establishes baseline settings for new tables, ensuring predictable behavior unless overridden. Below is a detailed table of default parameters, organized by configuration section, to help you understand the starting point for table behavior and appearance.
|
The `defaultConfig()` function (`config.go:defaultConfig`) establishes baseline settings for new tables, ensuring predictable behavior unless overridden. Below is a detailed table of default parameters, organized by configuration section, to help you understand the starting point for table behavior and appearance.
|
||||||
|
|
||||||
| Section | Parameter | Default Value | Description |
|
| Section | Parameter | Default Value | Description |
|
||||||
|---------------|-------------------------------|-----------------------------------|-----------------------------------------------------------------------------|
|
|---------------|--------------------------|-----------------------------------|-----------------------------------------------------------------------------|
|
||||||
| **Header** | `Alignment.Global` | `tw.AlignCenter` | Centers header text globally unless overridden by `PerColumn`. |
|
| **Header** | `Alignment.Global` | `tw.AlignCenter` | Centers header text globally unless overridden by `PerColumn`. |
|
||||||
| Header | `Alignment.PerColumn` | `[]tw.Align{}` | Empty; falls back to `Global` unless specified. |
|
| Header | `Alignment.PerColumn` | `[]tw.Align{}` | Empty; falls back to `Global` unless specified. |
|
||||||
| Header | `Formatting.AutoFormat` | `tw.On` | Applies title case (e.g., "col_one" → "COL ONE") to header content. |
|
| Header | `Formatting.AutoFormat` | `tw.On` | Applies title case (e.g., "col_one" → "COL ONE") to header content. |
|
||||||
| Header | `Formatting.AutoWrap` | `tw.WrapTruncate` | Truncates long header text with "…" based on width constraints. |
|
| Header | `Formatting.AutoWrap` | `tw.WrapTruncate` | Truncates long header text with "…" based on width constraints. |
|
||||||
| Header | `Formatting.MergeMode` | `tw.MergeNone` | Disables cell merging in headers by default. |
|
| Header | `Merging.Mode` | `tw.MergeNone` | Disables cell merging in headers by default. |
|
||||||
| Header | `Padding.Global` | `tw.PaddingDefault` (`" "`) | Adds one space on left and right of header cells. |
|
| Header | `Padding.Global` | `tw.PaddingDefault` (`" "`) | Adds one space on left and right of header cells. |
|
||||||
| Header | `Padding.PerColumn` | `[]tw.Padding{}` | Empty; falls back to `Global` unless specified. |
|
| Header | `Padding.PerColumn` | `[]tw.Padding{}` | Empty; falls back to `Global` unless specified. |
|
||||||
| Header | `ColMaxWidths.Global` | `0` (unlimited) | No maximum content width for header cells unless set. |
|
| Header | `ColMaxWidths.Global` | `0` (unlimited) | No maximum content width for header cells unless set. |
|
||||||
| Header | `ColMaxWidths.PerColumn` | `tw.NewMapper[int, int]()` | Empty map; no per-column content width limits unless specified. |
|
| Header | `ColMaxWidths.PerColumn` | `tw.NewMapper[int, int]()` | Empty map; no per-column content width limits unless specified. |
|
||||||
| Header | `Filter.Global` | `nil` | No global content transformation for header cells. |
|
| Header | `Filter.Global` | `nil` | No global content transformation for header cells. |
|
||||||
| Header | `Filter.PerColumn` | `[]func(string) string{}` | No per-column content transformations unless specified. |
|
| Header | `Filter.PerColumn` | `[]func(string) string{}` | No per-column content transformations unless specified. |
|
||||||
| **Row** | `Alignment.Global` | `tw.AlignLeft` | Left-aligns row text globally unless overridden by `PerColumn`. |
|
| **Row** | `Alignment.Global` | `tw.AlignLeft` | Left-aligns row text globally unless overridden by `PerColumn`. |
|
||||||
| Row | `Alignment.PerColumn` | `[]tw.Align{}` | Empty; falls back to `Global`. |
|
| Row | `Alignment.PerColumn` | `[]tw.Align{}` | Empty; falls back to `Global`. |
|
||||||
| Row | `Formatting.AutoFormat` | `tw.Off` | Disables auto-formatting (e.g., title case) for row content. |
|
| Row | `Formatting.AutoFormat` | `tw.Off` | Disables auto-formatting (e.g., title case) for row content. |
|
||||||
| Row | `Formatting.AutoWrap` | `tw.WrapNormal` | Wraps long row text naturally at word boundaries based on width constraints.|
|
| Row | `Formatting.AutoWrap` | `tw.WrapNormal` | Wraps long row text naturally at word boundaries based on width constraints.|
|
||||||
| Row | `Formatting.MergeMode` | `tw.MergeNone` | Disables cell merging in rows by default. |
|
| Row | `Merging.Mode` | `tw.MergeNone` | Disables cell merging in rows by default. |
|
||||||
| Row | `Padding.Global` | `tw.PaddingDefault` (`" "`) | Adds one space on left and right of row cells. |
|
| Row | `Padding.Global` | `tw.PaddingDefault` (`" "`) | Adds one space on left and right of row cells. |
|
||||||
| Row | `Padding.PerColumn` | `[]tw.Padding{}` | Empty; falls back to `Global`. |
|
| Row | `Padding.PerColumn` | `[]tw.Padding{}` | Empty; falls back to `Global`. |
|
||||||
| Row | `ColMaxWidths.Global` | `0` (unlimited) | No maximum content width for row cells. |
|
| Row | `ColMaxWidths.Global` | `0` (unlimited) | No maximum content width for row cells. |
|
||||||
| Row | `ColMaxWidths.PerColumn` | `tw.NewMapper[int, int]()` | Empty map; no per-column content width limits. |
|
| Row | `ColMaxWidths.PerColumn` | `tw.NewMapper[int, int]()` | Empty map; no per-column content width limits. |
|
||||||
| Row | `Filter.Global` | `nil` | No global content transformation for row cells. |
|
| Row | `Filter.Global` | `nil` | No global content transformation for row cells. |
|
||||||
| Row | `Filter.PerColumn` | `[]func(string) string{}` | No per-column content transformations. |
|
| Row | `Filter.PerColumn` | `[]func(string) string{}` | No per-column content transformations. |
|
||||||
| **Footer** | `Alignment.Global` | `tw.AlignRight` | Right-aligns footer text globally unless overridden by `PerColumn`. |
|
| **Footer** | `Alignment.Global` | `tw.AlignRight` | Right-aligns footer text globally unless overridden by `PerColumn`. |
|
||||||
| Footer | `Alignment.PerColumn` | `[]tw.Align{}` | Empty; falls back to `Global`. |
|
| Footer | `Alignment.PerColumn` | `[]tw.Align{}` | Empty; falls back to `Global`. |
|
||||||
| Footer | `Formatting.AutoFormat` | `tw.Off` | Disables auto-formatting for footer content. |
|
| Footer | `Formatting.AutoFormat` | `tw.Off` | Disables auto-formatting for footer content. |
|
||||||
| Footer | `Formatting.AutoWrap` | `tw.WrapNormal` | Wraps long footer text naturally. |
|
| Footer | `Formatting.AutoWrap` | `tw.WrapNormal` | Wraps long footer text naturally. |
|
||||||
| Footer | `Formatting.MergeMode` | `tw.MergeNone` | Disables cell merging in footers. |
|
| Footer | `Formatting.MergeMode` | `tw.MergeNone` | Disables cell merging in footers. |
|
||||||
| Footer | `Padding.Global` | `tw.PaddingDefault` (`" "`) | Adds one space on left and right of footer cells. |
|
| Footer | `Padding.Global` | `tw.PaddingDefault` (`" "`) | Adds one space on left and right of footer cells. |
|
||||||
| Footer | `Padding.PerColumn` | `[]tw.Padding{}` | Empty; falls back to `Global`. |
|
| Footer | `Padding.PerColumn` | `[]tw.Padding{}` | Empty; falls back to `Global`. |
|
||||||
| Footer | `ColMaxWidths.Global` | `0` (unlimited) | No maximum content width for footer cells. |
|
| Footer | `ColMaxWidths.Global` | `0` (unlimited) | No maximum content width for footer cells. |
|
||||||
| Footer | `ColMaxWidths.PerColumn` | `tw.NewMapper[int, int]()` | Empty map; no per-column content width limits. |
|
| Footer | `ColMaxWidths.PerColumn` | `tw.NewMapper[int, int]()` | Empty map; no per-column content width limits. |
|
||||||
| Footer | `Filter.Global` | `nil` | No global content transformation for footer cells. |
|
| Footer | `Filter.Global` | `nil` | No global content transformation for footer cells. |
|
||||||
| Footer | `Filter.PerColumn` | `[]func(string) string{}` | No per-column content transformations. |
|
| Footer | `Filter.PerColumn` | `[]func(string) string{}` | No per-column content transformations. |
|
||||||
| **Global** | `MaxWidth` | `0` (unlimited) | No overall table width limit. |
|
| **Global** | `MaxWidth` | `0` (unlimited) | No overall table width limit. |
|
||||||
| Global | `Behavior.AutoHide` | `tw.Off` | Displays empty columns (ignored in streaming). |
|
| Global | `Behavior.AutoHide` | `tw.Off` | Displays empty columns (ignored in streaming). |
|
||||||
| Global | `Behavior.TrimSpace` | `tw.On` | Trims leading/trailing spaces from cell content. |
|
| Global | `Behavior.TrimSpace` | `tw.On` | Trims leading/trailing spaces from cell content. |
|
||||||
| Global | `Behavior.Header` | `tw.Control{Hide: tw.Off}` | Shows header if content is provided. |
|
| Global | `Behavior.Header` | `tw.Control{Hide: tw.Off}` | Shows header if content is provided. |
|
||||||
| Global | `Behavior.Footer` | `tw.Control{Hide: tw.Off}` | Shows footer if content is provided. |
|
| Global | `Behavior.Footer` | `tw.Control{Hide: tw.Off}` | Shows footer if content is provided. |
|
||||||
| Global | `Behavior.Compact` | `tw.Compact{Merge: tw.Off}` | No compact width optimization for merged cells. |
|
| Global | `Behavior.Compact` | `tw.Compact{Merge: tw.Off}` | No compact width optimization for merged cells. |
|
||||||
| Global | `Debug` | `false` | Disables debug logging. |
|
| Global | `Debug` | `false` | Disables debug logging. |
|
||||||
| Global | `Stream.Enable` | `false` | Disables streaming mode by default. |
|
| Global | `Stream.Enable` | `false` | Disables streaming mode by default. |
|
||||||
| Global | `Widths.Global` | `0` (unlimited) | No fixed column width unless specified. |
|
| Global | `Widths.Global` | `0` (unlimited) | No fixed column width unless specified. |
|
||||||
| Global | `Widths.PerColumn` | `tw.NewMapper[int, int]()` | Empty map; no per-column fixed widths unless specified. |
|
| Global | `Widths.PerColumn` | `tw.NewMapper[int, int]()` | Empty map; no per-column fixed widths unless specified. |
|
||||||
|
|
||||||
**Notes**:
|
**Notes**:
|
||||||
- Defaults can be overridden using any configuration method.
|
- Defaults can be overridden using any configuration method.
|
||||||
|
|
@ -2210,7 +2210,7 @@ import (
|
||||||
func main() {
|
func main() {
|
||||||
// Horizontal Merging (Similar to v0.0.5)
|
// Horizontal Merging (Similar to v0.0.5)
|
||||||
tableH := tablewriter.NewTable(os.Stdout,
|
tableH := tablewriter.NewTable(os.Stdout,
|
||||||
tablewriter.WithConfig(tablewriter.Config{Row: tw.CellConfig{Formatting: tw.CellFormatting{MergeMode: tw.MergeHorizontal}}}),
|
tablewriter.WithConfig(tablewriter.Config{Row: tw.CellConfig{Merging: tw.CellMerging{Mode: tw.MergeHorizontal}}}),
|
||||||
tablewriter.WithRenderer(renderer.NewBlueprint(tw.Rendition{Symbols: tw.NewSymbols(tw.StyleASCII)})), // Specify renderer for symbols
|
tablewriter.WithRenderer(renderer.NewBlueprint(tw.Rendition{Symbols: tw.NewSymbols(tw.StyleASCII)})), // Specify renderer for symbols
|
||||||
)
|
)
|
||||||
tableH.Header("Category", "Item", "Item", "Notes") // Note: Two "Item" headers for demo
|
tableH.Header("Category", "Item", "Item", "Notes") // Note: Two "Item" headers for demo
|
||||||
|
|
@ -2219,7 +2219,7 @@ func main() {
|
||||||
|
|
||||||
// Vertical Merging
|
// Vertical Merging
|
||||||
tableV := tablewriter.NewTable(os.Stdout,
|
tableV := tablewriter.NewTable(os.Stdout,
|
||||||
tablewriter.WithConfig(tablewriter.Config{Row: tw.CellConfig{Formatting: tw.CellFormatting{MergeMode: tw.MergeVertical}}}),
|
tablewriter.WithConfig(tablewriter.Config{Row: tw.CellConfig{Merging: tw.CellMerging{Mode: tw.MergeVertical}}}),
|
||||||
tablewriter.WithRenderer(renderer.NewBlueprint(tw.Rendition{Symbols: tw.NewSymbols(tw.StyleASCII)})),
|
tablewriter.WithRenderer(renderer.NewBlueprint(tw.Rendition{Symbols: tw.NewSymbols(tw.StyleASCII)})),
|
||||||
)
|
)
|
||||||
tableV.Header("User", "Permission")
|
tableV.Header("User", "Permission")
|
||||||
|
|
@ -2230,7 +2230,7 @@ func main() {
|
||||||
|
|
||||||
// Hierarchical Merging
|
// Hierarchical Merging
|
||||||
tableHier := tablewriter.NewTable(os.Stdout,
|
tableHier := tablewriter.NewTable(os.Stdout,
|
||||||
tablewriter.WithConfig(tablewriter.Config{Row: tw.CellConfig{Formatting: tw.CellFormatting{MergeMode: tw.MergeHierarchical}}}),
|
tablewriter.WithConfig(tablewriter.Config{Row: tw.CellConfig{Merging: tw.CellMerging{Mode: tw.MergeHierarchical}}}),
|
||||||
tablewriter.WithRenderer(renderer.NewBlueprint(tw.Rendition{Symbols: tw.NewSymbols(tw.StyleASCII)})),
|
tablewriter.WithRenderer(renderer.NewBlueprint(tw.Rendition{Symbols: tw.NewSymbols(tw.StyleASCII)})),
|
||||||
)
|
)
|
||||||
tableHier.Header("Group", "SubGroup", "Item")
|
tableHier.Header("Group", "SubGroup", "Item")
|
||||||
|
|
|
||||||
20
vendor/github.com/olekukonko/tablewriter/README.md
generated
vendored
20
vendor/github.com/olekukonko/tablewriter/README.md
generated
vendored
|
|
@ -28,7 +28,7 @@ go get github.com/olekukonko/tablewriter@v0.0.5
|
||||||
#### Latest Version
|
#### Latest Version
|
||||||
The latest stable version
|
The latest stable version
|
||||||
```bash
|
```bash
|
||||||
go get github.com/olekukonko/tablewriter@v1.1.0
|
go get github.com/olekukonko/tablewriter@v1.1.2
|
||||||
```
|
```
|
||||||
|
|
||||||
**Warning:** Version `v1.0.0` contains missing functionality and should not be used.
|
**Warning:** Version `v1.0.0` contains missing functionality and should not be used.
|
||||||
|
|
@ -62,7 +62,7 @@ func main() {
|
||||||
data := [][]string{
|
data := [][]string{
|
||||||
{"Package", "Version", "Status"},
|
{"Package", "Version", "Status"},
|
||||||
{"tablewriter", "v0.0.5", "legacy"},
|
{"tablewriter", "v0.0.5", "legacy"},
|
||||||
{"tablewriter", "v1.1.0", "latest"},
|
{"tablewriter", "v1.1.2", "latest"},
|
||||||
}
|
}
|
||||||
|
|
||||||
table := tablewriter.NewWriter(os.Stdout)
|
table := tablewriter.NewWriter(os.Stdout)
|
||||||
|
|
@ -77,7 +77,7 @@ func main() {
|
||||||
│ PACKAGE │ VERSION │ STATUS │
|
│ PACKAGE │ VERSION │ STATUS │
|
||||||
├─────────────┼─────────┼────────┤
|
├─────────────┼─────────┼────────┤
|
||||||
│ tablewriter │ v0.0.5 │ legacy │
|
│ tablewriter │ v0.0.5 │ legacy │
|
||||||
│ tablewriter │ v1.1.0 │ latest │
|
│ tablewriter │ v1.1.2 │ latest │
|
||||||
└─────────────┴─────────┴────────┘
|
└─────────────┴─────────┴────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -520,7 +520,7 @@ func main() {
|
||||||
tablewriter.WithConfig(tablewriter.Config{
|
tablewriter.WithConfig(tablewriter.Config{
|
||||||
Header: tw.CellConfig{Alignment: tw.CellAlignment{Global: tw.AlignCenter}},
|
Header: tw.CellConfig{Alignment: tw.CellAlignment{Global: tw.AlignCenter}},
|
||||||
Row: tw.CellConfig{
|
Row: tw.CellConfig{
|
||||||
Formatting: tw.CellFormatting{MergeMode: tw.MergeHierarchical},
|
Merging: tw.CellMerging{Mode: tw.MergeHierarchical},
|
||||||
Alignment: tw.CellAlignment{Global: tw.AlignLeft},
|
Alignment: tw.CellAlignment{Global: tw.AlignLeft},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
@ -579,8 +579,8 @@ func main() {
|
||||||
})),
|
})),
|
||||||
tablewriter.WithConfig(tablewriter.Config{
|
tablewriter.WithConfig(tablewriter.Config{
|
||||||
Row: tw.CellConfig{
|
Row: tw.CellConfig{
|
||||||
Formatting: tw.CellFormatting{MergeMode: tw.MergeBoth},
|
Merging: tw.CellMerging{Mode: tw.MergeBoth},
|
||||||
Alignment: tw.CellAlignment{PerColumn: []tw.Align{tw.Skip, tw.Skip, tw.AlignRight, tw.AlignLeft}},
|
Alignment: tw.CellAlignment{PerColumn: []tw.Align{tw.Skip, tw.Skip, tw.AlignRight, tw.AlignLeft}},
|
||||||
},
|
},
|
||||||
|
|
||||||
Footer: tw.CellConfig{
|
Footer: tw.CellConfig{
|
||||||
|
|
@ -806,12 +806,12 @@ func main() {
|
||||||
tablewriter.WithRenderer(renderer.NewHTML(htmlCfg)),
|
tablewriter.WithRenderer(renderer.NewHTML(htmlCfg)),
|
||||||
tablewriter.WithConfig(tablewriter.Config{
|
tablewriter.WithConfig(tablewriter.Config{
|
||||||
Header: tw.CellConfig{
|
Header: tw.CellConfig{
|
||||||
Formatting: tw.CellFormatting{MergeMode: tw.MergeHorizontal}, // Merge identical header cells
|
Merging: tw.CellMerging{Mode: tw.MergeHorizontal}, // Merge identical header cells
|
||||||
Alignment: tw.CellAlignment{Global: tw.AlignCenter},
|
Alignment: tw.CellAlignment{Global: tw.AlignCenter},
|
||||||
},
|
},
|
||||||
Row: tw.CellConfig{
|
Row: tw.CellConfig{
|
||||||
Formatting: tw.CellFormatting{MergeMode: tw.MergeHorizontal}, // Merge identical row cells
|
Merging: tw.CellMerging{Mode: tw.MergeHorizontal}, // Merge identical row cells
|
||||||
Alignment: tw.CellAlignment{Global: tw.AlignLeft},
|
Alignment: tw.CellAlignment{Global: tw.AlignLeft},
|
||||||
},
|
},
|
||||||
Footer: tw.CellConfig{Alignment: tw.CellAlignment{Global: tw.AlignRight}},
|
Footer: tw.CellConfig{Alignment: tw.CellAlignment{Global: tw.AlignRight}},
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
194
vendor/github.com/olekukonko/tablewriter/benchstat.txt
generated
vendored
Normal file
194
vendor/github.com/olekukonko/tablewriter/benchstat.txt
generated
vendored
Normal file
|
|
@ -0,0 +1,194 @@
|
||||||
|
goos: darwin
|
||||||
|
goarch: arm64
|
||||||
|
pkg: github.com/olekukonko/tablewriter/pkg/twwarp
|
||||||
|
cpu: Apple M2
|
||||||
|
│ old.txt │ new.txt │
|
||||||
|
│ sec/op │ sec/op vs base │
|
||||||
|
WrapString-8 112.8µ ± 1% 112.9µ ± 2% ~ (p=0.589 n=6)
|
||||||
|
WrapStringWithSpaces-8 113.4µ ± 1% 113.7µ ± 1% ~ (p=0.310 n=6)
|
||||||
|
geomean 113.1µ 113.3µ +0.15%
|
||||||
|
|
||||||
|
│ old.txt │ new.txt │
|
||||||
|
│ B/s │ B/s vs base │
|
||||||
|
WrapString-8 84.92Mi ± 1% 84.82Mi ± 2% ~ (p=0.589 n=6)
|
||||||
|
WrapStringWithSpaces-8 84.43Mi ± 1% 84.27Mi ± 1% ~ (p=0.310 n=6)
|
||||||
|
geomean 84.68Mi 84.55Mi -0.15%
|
||||||
|
|
||||||
|
│ old.txt │ new.txt │
|
||||||
|
│ B/op │ B/op vs base │
|
||||||
|
WrapString-8 47.35Ki ± 0% 47.35Ki ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WrapStringWithSpaces-8 52.76Ki ± 0% 52.76Ki ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
geomean 49.98Ki 49.98Ki +0.00%
|
||||||
|
¹ all samples are equal
|
||||||
|
|
||||||
|
│ old.txt │ new.txt │
|
||||||
|
│ allocs/op │ allocs/op vs base │
|
||||||
|
WrapString-8 33.00 ± 0% 33.00 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WrapStringWithSpaces-8 51.00 ± 0% 51.00 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
geomean 41.02 41.02 +0.00%
|
||||||
|
¹ all samples are equal
|
||||||
|
|
||||||
|
pkg: github.com/olekukonko/tablewriter/pkg/twwidth
|
||||||
|
│ old.txt │ new.txt │
|
||||||
|
│ sec/op │ sec/op vs base │
|
||||||
|
WidthFunction/SimpleASCII_EAfalse_NoCache-8 387.6n ± 1% 368.4n ± 2% -4.97% (p=0.002 n=6)
|
||||||
|
WidthFunction/SimpleASCII_EAfalse_CacheMiss-8 219.0n ± 127% 217.5n ± 119% ~ (p=0.372 n=6)
|
||||||
|
WidthFunction/SimpleASCII_EAfalse_CacheHit-8 14.78n ± 1% 14.54n ± 3% ~ (p=0.061 n=6)
|
||||||
|
WidthFunction/SimpleASCII_EAtrue_NoCache-8 676.4n ± 1% 366.8n ± 2% -45.77% (p=0.002 n=6)
|
||||||
|
WidthFunction/SimpleASCII_EAtrue_CacheMiss-8 216.1n ± 375% 216.0n ± 128% ~ (p=0.937 n=6)
|
||||||
|
WidthFunction/SimpleASCII_EAtrue_CacheHit-8 14.71n ± 0% 14.49n ± 0% -1.53% (p=0.002 n=6)
|
||||||
|
WidthFunction/ASCIIWithANSI_EAfalse_NoCache-8 1.027µ ± 3% 1.007µ ± 1% -2.00% (p=0.002 n=6)
|
||||||
|
WidthFunction/ASCIIWithANSI_EAfalse_CacheMiss-8 219.5n ± 516% 221.4n ± 502% ~ (p=0.515 n=6)
|
||||||
|
WidthFunction/ASCIIWithANSI_EAfalse_CacheHit-8 14.81n ± 1% 14.61n ± 1% -1.35% (p=0.009 n=6)
|
||||||
|
WidthFunction/ASCIIWithANSI_EAtrue_NoCache-8 1.313µ ± 2% 1.009µ ± 2% -23.15% (p=0.002 n=6)
|
||||||
|
WidthFunction/ASCIIWithANSI_EAtrue_CacheMiss-8 653.2n ± 150% 218.2n ± 524% ~ (p=0.331 n=6)
|
||||||
|
WidthFunction/ASCIIWithANSI_EAtrue_CacheHit-8 14.73n ± 2% 14.50n ± 0% -1.60% (p=0.002 n=6)
|
||||||
|
WidthFunction/EastAsian_EAfalse_NoCache-8 747.3n ± 1% 336.2n ± 1% -55.02% (p=0.002 n=6)
|
||||||
|
WidthFunction/EastAsian_EAfalse_CacheMiss-8 226.3n ± 384% 227.4n ± 113% ~ (p=0.937 n=6)
|
||||||
|
WidthFunction/EastAsian_EAfalse_CacheHit-8 14.74n ± 1% 14.58n ± 1% -1.09% (p=0.011 n=6)
|
||||||
|
WidthFunction/EastAsian_EAtrue_NoCache-8 965.4n ± 2% 348.7n ± 0% -63.88% (p=0.002 n=6)
|
||||||
|
WidthFunction/EastAsian_EAtrue_CacheMiss-8 225.4n ± 511% 225.8n ± 111% ~ (p=1.000 n=6)
|
||||||
|
WidthFunction/EastAsian_EAtrue_CacheHit-8 14.72n ± 1% 14.54n ± 3% ~ (p=0.056 n=6)
|
||||||
|
WidthFunction/EastAsianWithANSI_EAfalse_NoCache-8 1376.0n ± 2% 983.8n ± 2% -28.50% (p=0.002 n=6)
|
||||||
|
WidthFunction/EastAsianWithANSI_EAfalse_CacheMiss-8 633.6n ± 170% 222.4n ± 513% ~ (p=0.974 n=6)
|
||||||
|
WidthFunction/EastAsianWithANSI_EAfalse_CacheHit-8 15.73n ± 1% 15.64n ± 1% ~ (p=0.227 n=6)
|
||||||
|
WidthFunction/EastAsianWithANSI_EAtrue_NoCache-8 1589.5n ± 1% 996.9n ± 2% -37.29% (p=0.002 n=6)
|
||||||
|
WidthFunction/EastAsianWithANSI_EAtrue_CacheMiss-8 484.8n ± 309% 221.3n ± 516% ~ (p=0.240 n=6)
|
||||||
|
WidthFunction/EastAsianWithANSI_EAtrue_CacheHit-8 15.74n ± 1% 15.73n ± 1% ~ (p=0.485 n=6)
|
||||||
|
WidthFunction/LongSimpleASCII_EAfalse_NoCache-8 4.916µ ± 3% 4.512µ ± 4% -8.22% (p=0.002 n=6)
|
||||||
|
WidthFunction/LongSimpleASCII_EAfalse_CacheMiss-8 2.430µ ± 114% 2.182µ ± 123% ~ (p=0.699 n=6)
|
||||||
|
WidthFunction/LongSimpleASCII_EAfalse_CacheHit-8 23.75n ± 3% 23.24n ± 3% ~ (p=0.065 n=6)
|
||||||
|
WidthFunction/LongSimpleASCII_EAtrue_NoCache-8 9.273µ ± 1% 4.519µ ± 1% -51.27% (p=0.002 n=6)
|
||||||
|
WidthFunction/LongSimpleASCII_EAtrue_CacheMiss-8 4.021µ ± 131% 2.127µ ± 128% ~ (p=0.240 n=6)
|
||||||
|
WidthFunction/LongSimpleASCII_EAtrue_CacheHit-8 23.50n ± 2% 23.48n ± 1% ~ (p=0.589 n=6)
|
||||||
|
WidthFunction/LongASCIIWithANSI_EAfalse_NoCache-8 57.36µ ± 1% 57.33µ ± 2% ~ (p=0.818 n=6)
|
||||||
|
WidthFunction/LongASCIIWithANSI_EAfalse_CacheMiss-8 22.18µ ± 135% 14.55µ ± 299% ~ (p=0.589 n=6)
|
||||||
|
WidthFunction/LongASCIIWithANSI_EAfalse_CacheHit-8 44.21n ± 1% 44.20n ± 2% ~ (p=0.818 n=6)
|
||||||
|
WidthFunction/LongASCIIWithANSI_EAtrue_NoCache-8 60.25µ ± 2% 57.90µ ± 2% -3.90% (p=0.002 n=6)
|
||||||
|
WidthFunction/LongASCIIWithANSI_EAtrue_CacheMiss-8 16.11µ ± 263% 20.02µ ± 183% ~ (p=0.699 n=6)
|
||||||
|
WidthFunction/LongASCIIWithANSI_EAtrue_CacheHit-8 44.57n ± 1% 44.18n ± 2% ~ (p=0.461 n=6)
|
||||||
|
geomean 358.5n 283.9n -20.82%
|
||||||
|
|
||||||
|
│ old.txt │ new.txt │
|
||||||
|
│ B/s │ B/s vs base │
|
||||||
|
WidthFunction/SimpleASCII_EAfalse_NoCache-8 86.11Mi ± 1% 90.63Mi ± 2% +5.24% (p=0.002 n=6)
|
||||||
|
WidthFunction/SimpleASCII_EAfalse_CacheMiss-8 152.4Mi ± 56% 153.5Mi ± 54% ~ (p=0.394 n=6)
|
||||||
|
WidthFunction/SimpleASCII_EAfalse_CacheHit-8 2.205Gi ± 1% 2.242Gi ± 3% ~ (p=0.065 n=6)
|
||||||
|
WidthFunction/SimpleASCII_EAtrue_NoCache-8 49.35Mi ± 1% 91.00Mi ± 2% +84.40% (p=0.002 n=6)
|
||||||
|
WidthFunction/SimpleASCII_EAtrue_CacheMiss-8 154.5Mi ± 79% 154.5Mi ± 56% ~ (p=0.937 n=6)
|
||||||
|
WidthFunction/SimpleASCII_EAtrue_CacheHit-8 2.215Gi ± 0% 2.250Gi ± 0% +1.58% (p=0.002 n=6)
|
||||||
|
WidthFunction/ASCIIWithANSI_EAfalse_NoCache-8 56.66Mi ± 2% 57.78Mi ± 1% +1.99% (p=0.002 n=6)
|
||||||
|
WidthFunction/ASCIIWithANSI_EAfalse_CacheMiss-8 265.1Mi ± 84% 262.7Mi ± 83% ~ (p=0.485 n=6)
|
||||||
|
WidthFunction/ASCIIWithANSI_EAfalse_CacheHit-8 3.836Gi ± 1% 3.888Gi ± 1% +1.34% (p=0.009 n=6)
|
||||||
|
WidthFunction/ASCIIWithANSI_EAtrue_NoCache-8 44.30Mi ± 2% 57.65Mi ± 2% +30.14% (p=0.002 n=6)
|
||||||
|
WidthFunction/ASCIIWithANSI_EAtrue_CacheMiss-8 147.3Mi ± 81% 266.7Mi ± 84% ~ (p=0.310 n=6)
|
||||||
|
WidthFunction/ASCIIWithANSI_EAtrue_CacheHit-8 3.856Gi ± 2% 3.919Gi ± 0% +1.63% (p=0.002 n=6)
|
||||||
|
WidthFunction/EastAsian_EAfalse_NoCache-8 76.58Mi ± 1% 170.21Mi ± 1% +122.28% (p=0.002 n=6)
|
||||||
|
WidthFunction/EastAsian_EAfalse_CacheMiss-8 252.8Mi ± 79% 251.6Mi ± 53% ~ (p=0.937 n=6)
|
||||||
|
WidthFunction/EastAsian_EAfalse_CacheHit-8 3.791Gi ± 1% 3.832Gi ± 1% +1.08% (p=0.009 n=6)
|
||||||
|
WidthFunction/EastAsian_EAtrue_NoCache-8 59.27Mi ± 2% 164.10Mi ± 0% +176.87% (p=0.002 n=6)
|
||||||
|
WidthFunction/EastAsian_EAtrue_CacheMiss-8 253.9Mi ± 84% 253.4Mi ± 53% ~ (p=1.000 n=6)
|
||||||
|
WidthFunction/EastAsian_EAtrue_CacheHit-8 3.796Gi ± 1% 3.841Gi ± 3% ~ (p=0.065 n=6)
|
||||||
|
WidthFunction/EastAsianWithANSI_EAfalse_NoCache-8 60.29Mi ± 1% 84.33Mi ± 2% +39.88% (p=0.002 n=6)
|
||||||
|
WidthFunction/EastAsianWithANSI_EAfalse_CacheMiss-8 227.1Mi ± 79% 373.2Mi ± 84% ~ (p=1.000 n=6)
|
||||||
|
WidthFunction/EastAsianWithANSI_EAfalse_CacheHit-8 5.154Gi ± 1% 5.181Gi ± 1% ~ (p=0.240 n=6)
|
||||||
|
WidthFunction/EastAsianWithANSI_EAtrue_NoCache-8 52.19Mi ± 1% 83.23Mi ± 2% +59.47% (p=0.002 n=6)
|
||||||
|
WidthFunction/EastAsianWithANSI_EAtrue_CacheMiss-8 230.9Mi ± 82% 374.9Mi ± 84% ~ (p=0.240 n=6)
|
||||||
|
WidthFunction/EastAsianWithANSI_EAtrue_CacheHit-8 5.147Gi ± 1% 5.152Gi ± 1% ~ (p=0.485 n=6)
|
||||||
|
WidthFunction/LongSimpleASCII_EAfalse_NoCache-8 104.8Mi ± 3% 114.1Mi ± 4% +8.95% (p=0.002 n=6)
|
||||||
|
WidthFunction/LongSimpleASCII_EAfalse_CacheMiss-8 368.0Mi ± 293% 474.3Mi ± 211% ~ (p=0.699 n=6)
|
||||||
|
WidthFunction/LongSimpleASCII_EAfalse_CacheHit-8 21.17Gi ± 3% 21.64Gi ± 2% ~ (p=0.065 n=6)
|
||||||
|
WidthFunction/LongSimpleASCII_EAtrue_NoCache-8 55.54Mi ± 1% 113.97Mi ± 1% +105.21% (p=0.002 n=6)
|
||||||
|
WidthFunction/LongSimpleASCII_EAtrue_CacheMiss-8 399.8Mi ± 232% 577.5Mi ± 149% ~ (p=0.240 n=6)
|
||||||
|
WidthFunction/LongSimpleASCII_EAtrue_CacheHit-8 21.40Gi ± 2% 21.41Gi ± 1% ~ (p=0.589 n=6)
|
||||||
|
WidthFunction/LongASCIIWithANSI_EAfalse_NoCache-8 34.08Mi ± 1% 34.10Mi ± 2% ~ (p=0.784 n=6)
|
||||||
|
WidthFunction/LongASCIIWithANSI_EAfalse_CacheMiss-8 101.5Mi ± 1396% 643.9Mi ± 320% ~ (p=0.589 n=6)
|
||||||
|
WidthFunction/LongASCIIWithANSI_EAfalse_CacheHit-8 43.18Gi ± 1% 43.20Gi ± 2% ~ (p=0.818 n=6)
|
||||||
|
WidthFunction/LongASCIIWithANSI_EAtrue_NoCache-8 32.45Mi ± 2% 33.76Mi ± 2% +4.06% (p=0.002 n=6)
|
||||||
|
WidthFunction/LongASCIIWithANSI_EAtrue_CacheMiss-8 393.0Mi ± 296% 122.4Mi ± 1610% ~ (p=0.699 n=6)
|
||||||
|
WidthFunction/LongASCIIWithANSI_EAtrue_CacheHit-8 42.83Gi ± 1% 43.21Gi ± 2% ~ (p=0.485 n=6)
|
||||||
|
geomean 456.4Mi 560.6Mi +22.83%
|
||||||
|
|
||||||
|
│ old.txt │ new.txt │
|
||||||
|
│ B/op │ B/op vs base │
|
||||||
|
WidthFunction/SimpleASCII_EAfalse_NoCache-8 112.0 ± 1% 113.0 ± 0% ~ (p=0.061 n=6)
|
||||||
|
WidthFunction/SimpleASCII_EAfalse_CacheMiss-8 55.00 ± 200% 55.00 ± 202% ~ (p=1.000 n=6)
|
||||||
|
WidthFunction/SimpleASCII_EAfalse_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/SimpleASCII_EAtrue_NoCache-8 113.0 ± 1% 113.0 ± 0% ~ (p=1.000 n=6)
|
||||||
|
WidthFunction/SimpleASCII_EAtrue_CacheMiss-8 55.00 ± 505% 55.00 ± 205% ~ (p=0.697 n=6)
|
||||||
|
WidthFunction/SimpleASCII_EAtrue_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/ASCIIWithANSI_EAfalse_NoCache-8 185.0 ± 0% 185.0 ± 1% ~ (p=0.455 n=6)
|
||||||
|
WidthFunction/ASCIIWithANSI_EAfalse_CacheMiss-8 87.00 ± 402% 87.00 ± 401% ~ (p=1.000 n=6)
|
||||||
|
WidthFunction/ASCIIWithANSI_EAfalse_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/ASCIIWithANSI_EAtrue_NoCache-8 185.0 ± 0% 185.0 ± 1% ~ (p=1.000 n=6)
|
||||||
|
WidthFunction/ASCIIWithANSI_EAtrue_CacheMiss-8 174.00 ± 115% 87.00 ± 401% ~ (p=0.621 n=6)
|
||||||
|
WidthFunction/ASCIIWithANSI_EAtrue_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/EastAsian_EAfalse_NoCache-8 145.0 ± 0% 146.0 ± 0% +0.69% (p=0.002 n=6)
|
||||||
|
WidthFunction/EastAsian_EAfalse_CacheMiss-8 87.00 ± 392% 87.00 ± 167% ~ (p=0.697 n=6)
|
||||||
|
WidthFunction/EastAsian_EAfalse_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/EastAsian_EAtrue_NoCache-8 145.0 ± 1% 146.0 ± 1% +0.69% (p=0.013 n=6)
|
||||||
|
WidthFunction/EastAsian_EAtrue_CacheMiss-8 87.00 ± 392% 87.00 ± 164% ~ (p=0.697 n=6)
|
||||||
|
WidthFunction/EastAsian_EAtrue_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/EastAsianWithANSI_EAfalse_NoCache-8 193.0 ± 1% 193.0 ± 0% ~ (p=1.000 n=6)
|
||||||
|
WidthFunction/EastAsianWithANSI_EAfalse_CacheMiss-8 232.0 ± 134% 103.0 ± 485% ~ (p=0.924 n=6)
|
||||||
|
WidthFunction/EastAsianWithANSI_EAfalse_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/EastAsianWithANSI_EAtrue_NoCache-8 193.0 ± 0% 193.0 ± 1% ~ (p=1.000 n=6)
|
||||||
|
WidthFunction/EastAsianWithANSI_EAtrue_CacheMiss-8 185.0 ± 203% 103.0 ± 485% ~ (p=0.621 n=6)
|
||||||
|
WidthFunction/EastAsianWithANSI_EAtrue_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/LongSimpleASCII_EAfalse_NoCache-8 1.153Ki ± 0% 1.150Ki ± 0% ~ (p=0.126 n=6)
|
||||||
|
WidthFunction/LongSimpleASCII_EAfalse_CacheMiss-8 1.050Ki ± 72% 1.047Ki ± 74% ~ (p=0.939 n=6)
|
||||||
|
WidthFunction/LongSimpleASCII_EAfalse_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/LongSimpleASCII_EAtrue_NoCache-8 1.152Ki ± 0% 1.155Ki ± 0% +0.30% (p=0.015 n=6)
|
||||||
|
WidthFunction/LongSimpleASCII_EAtrue_CacheMiss-8 1.036Ki ± 71% 1.039Ki ± 76% ~ (p=0.981 n=6)
|
||||||
|
WidthFunction/LongSimpleASCII_EAtrue_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/LongASCIIWithANSI_EAfalse_NoCache-8 1.355Ki ± 0% 1.358Ki ± 0% ~ (p=0.065 n=6)
|
||||||
|
WidthFunction/LongASCIIWithANSI_EAfalse_CacheMiss-8 2.787Ki ± 31% 2.613Ki ± 43% ~ (p=0.805 n=6)
|
||||||
|
WidthFunction/LongASCIIWithANSI_EAfalse_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/LongASCIIWithANSI_EAtrue_NoCache-8 1.358Ki ± 0% 1.361Ki ± 0% ~ (p=0.158 n=6)
|
||||||
|
WidthFunction/LongASCIIWithANSI_EAtrue_CacheMiss-8 2.625Ki ± 43% 2.741Ki ± 37% ~ (p=0.987 n=6)
|
||||||
|
WidthFunction/LongASCIIWithANSI_EAtrue_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
geomean ² -5.62% ²
|
||||||
|
¹ all samples are equal
|
||||||
|
² summaries must be >0 to compute geomean
|
||||||
|
|
||||||
|
│ old.txt │ new.txt │
|
||||||
|
│ allocs/op │ allocs/op vs base │
|
||||||
|
WidthFunction/SimpleASCII_EAfalse_NoCache-8 3.000 ± 0% 3.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/SimpleASCII_EAfalse_CacheMiss-8 1.000 ± 200% 1.000 ± 200% ~ (p=1.000 n=6)
|
||||||
|
WidthFunction/SimpleASCII_EAfalse_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/SimpleASCII_EAtrue_NoCache-8 3.000 ± 0% 3.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/SimpleASCII_EAtrue_CacheMiss-8 1.000 ± 300% 1.000 ± 200% ~ (p=0.697 n=6)
|
||||||
|
WidthFunction/SimpleASCII_EAtrue_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/ASCIIWithANSI_EAfalse_NoCache-8 6.000 ± 0% 6.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/ASCIIWithANSI_EAfalse_CacheMiss-8 1.000 ± 600% 1.000 ± 600% ~ (p=1.000 n=6)
|
||||||
|
WidthFunction/ASCIIWithANSI_EAfalse_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/ASCIIWithANSI_EAtrue_NoCache-8 6.000 ± 0% 6.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/ASCIIWithANSI_EAtrue_CacheMiss-8 3.500 ± 100% 1.000 ± 600% ~ (p=0.610 n=6)
|
||||||
|
WidthFunction/ASCIIWithANSI_EAtrue_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/EastAsian_EAfalse_NoCache-8 3.000 ± 0% 3.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/EastAsian_EAfalse_CacheMiss-8 1.000 ± 300% 1.000 ± 200% ~ (p=0.697 n=6)
|
||||||
|
WidthFunction/EastAsian_EAfalse_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/EastAsian_EAtrue_NoCache-8 3.000 ± 0% 3.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/EastAsian_EAtrue_CacheMiss-8 1.000 ± 300% 1.000 ± 200% ~ (p=0.697 n=6)
|
||||||
|
WidthFunction/EastAsian_EAtrue_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/EastAsianWithANSI_EAfalse_NoCache-8 5.000 ± 0% 5.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/EastAsianWithANSI_EAfalse_CacheMiss-8 3.000 ± 133% 1.000 ± 600% ~ (p=1.000 n=6)
|
||||||
|
WidthFunction/EastAsianWithANSI_EAfalse_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/EastAsianWithANSI_EAtrue_NoCache-8 5.000 ± 0% 5.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/EastAsianWithANSI_EAtrue_CacheMiss-8 2.500 ± 180% 1.000 ± 600% ~ (p=0.610 n=6)
|
||||||
|
WidthFunction/EastAsianWithANSI_EAtrue_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/LongSimpleASCII_EAfalse_NoCache-8 3.000 ± 0% 3.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/LongSimpleASCII_EAfalse_CacheMiss-8 3.000 ± 67% 3.000 ± 67% ~ (p=1.000 n=6)
|
||||||
|
WidthFunction/LongSimpleASCII_EAfalse_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/LongSimpleASCII_EAtrue_NoCache-8 3.000 ± 0% 3.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/LongSimpleASCII_EAtrue_CacheMiss-8 3.000 ± 67% 3.000 ± 67% ~ (p=1.000 n=6)
|
||||||
|
WidthFunction/LongSimpleASCII_EAtrue_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/LongASCIIWithANSI_EAfalse_NoCache-8 9.000 ± 0% 9.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/LongASCIIWithANSI_EAfalse_CacheMiss-8 5.000 ± 100% 3.500 ± 186% ~ (p=0.978 n=6)
|
||||||
|
WidthFunction/LongASCIIWithANSI_EAfalse_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/LongASCIIWithANSI_EAtrue_NoCache-8 9.000 ± 0% 9.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
WidthFunction/LongASCIIWithANSI_EAtrue_CacheMiss-8 4.000 ± 150% 4.500 ± 122% ~ (p=0.952 n=6)
|
||||||
|
WidthFunction/LongASCIIWithANSI_EAtrue_CacheHit-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹
|
||||||
|
geomean ² -9.28% ²
|
||||||
|
¹ all samples are equal
|
||||||
|
² summaries must be >0 to compute geomean
|
||||||
147
vendor/github.com/olekukonko/tablewriter/config.go
generated
vendored
147
vendor/github.com/olekukonko/tablewriter/config.go
generated
vendored
|
|
@ -135,12 +135,12 @@ func (b *ConfigBuilder) WithFooterMaxWidth(maxWidth int) *ConfigBuilder {
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithFooterMergeMode sets the merge behavior for footer cells (e.g., horizontal, hierarchical).
|
// Deprecated: Use .Footer().CellMerging().WithMode(...) instead. This method will be removed in a future version.
|
||||||
// Invalid merge modes are ignored.
|
|
||||||
func (b *ConfigBuilder) WithFooterMergeMode(mergeMode int) *ConfigBuilder {
|
func (b *ConfigBuilder) WithFooterMergeMode(mergeMode int) *ConfigBuilder {
|
||||||
if mergeMode < tw.MergeNone || mergeMode > tw.MergeHierarchical {
|
if mergeMode < tw.MergeNone || mergeMode > tw.MergeHierarchical {
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
b.config.Footer.Merging.Mode = mergeMode
|
||||||
b.config.Footer.Formatting.MergeMode = mergeMode
|
b.config.Footer.Formatting.MergeMode = mergeMode
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
@ -187,12 +187,12 @@ func (b *ConfigBuilder) WithHeaderMaxWidth(maxWidth int) *ConfigBuilder {
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithHeaderMergeMode sets the merge behavior for header cells (e.g., horizontal, vertical).
|
// Deprecated: Use .Header().CellMerging().WithMode(...) instead. This method will be removed in a future version.
|
||||||
// Invalid merge modes are ignored.
|
|
||||||
func (b *ConfigBuilder) WithHeaderMergeMode(mergeMode int) *ConfigBuilder {
|
func (b *ConfigBuilder) WithHeaderMergeMode(mergeMode int) *ConfigBuilder {
|
||||||
if mergeMode < tw.MergeNone || mergeMode > tw.MergeHierarchical {
|
if mergeMode < tw.MergeNone || mergeMode > tw.MergeHierarchical {
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
b.config.Header.Merging.Mode = mergeMode
|
||||||
b.config.Header.Formatting.MergeMode = mergeMode
|
b.config.Header.Formatting.MergeMode = mergeMode
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
@ -246,12 +246,12 @@ func (b *ConfigBuilder) WithRowMaxWidth(maxWidth int) *ConfigBuilder {
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithRowMergeMode sets the merge behavior for row cells (e.g., horizontal, hierarchical).
|
// Deprecated: Use .Row().CellMerging().WithMode(...) instead. This method will be removed in a future version.
|
||||||
// Invalid merge modes are ignored.
|
|
||||||
func (b *ConfigBuilder) WithRowMergeMode(mergeMode int) *ConfigBuilder {
|
func (b *ConfigBuilder) WithRowMergeMode(mergeMode int) *ConfigBuilder {
|
||||||
if mergeMode < tw.MergeNone || mergeMode > tw.MergeHierarchical {
|
if mergeMode < tw.MergeNone || mergeMode > tw.MergeHierarchical {
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
b.config.Row.Merging.Mode = mergeMode
|
||||||
b.config.Row.Formatting.MergeMode = mergeMode
|
b.config.Row.Formatting.MergeMode = mergeMode
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
@ -285,6 +285,14 @@ func (h *HeaderConfigBuilder) Formatting() *HeaderFormattingBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Merging returns a HeaderMergingBuilder for configuring cell merging.
|
||||||
|
func (h *HeaderConfigBuilder) Merging() *HeaderMergingBuilder {
|
||||||
|
return &HeaderMergingBuilder{
|
||||||
|
parent: h,
|
||||||
|
config: &h.config.Merging,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Padding returns a HeaderPaddingBuilder for header padding
|
// Padding returns a HeaderPaddingBuilder for header padding
|
||||||
func (h *HeaderConfigBuilder) Padding() *HeaderPaddingBuilder {
|
func (h *HeaderConfigBuilder) Padding() *HeaderPaddingBuilder {
|
||||||
return &HeaderPaddingBuilder{
|
return &HeaderPaddingBuilder{
|
||||||
|
|
@ -341,6 +349,14 @@ func (r *RowConfigBuilder) Formatting() *RowFormattingBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Merging returns a RowMergingBuilder for configuring cell merging.
|
||||||
|
func (r *RowConfigBuilder) Merging() *RowMergingBuilder {
|
||||||
|
return &RowMergingBuilder{
|
||||||
|
parent: r,
|
||||||
|
config: &r.config.Merging,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Padding returns a RowPaddingBuilder for row padding
|
// Padding returns a RowPaddingBuilder for row padding
|
||||||
func (r *RowConfigBuilder) Padding() *RowPaddingBuilder {
|
func (r *RowConfigBuilder) Padding() *RowPaddingBuilder {
|
||||||
return &RowPaddingBuilder{
|
return &RowPaddingBuilder{
|
||||||
|
|
@ -397,6 +413,14 @@ func (f *FooterConfigBuilder) Formatting() *FooterFormattingBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Merging returns a FooterMergingBuilder for configuring cell merging.
|
||||||
|
func (f *FooterConfigBuilder) Merging() *FooterMergingBuilder {
|
||||||
|
return &FooterMergingBuilder{
|
||||||
|
parent: f,
|
||||||
|
config: &f.config.Merging,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Padding returns a FooterPaddingBuilder for footer padding
|
// Padding returns a FooterPaddingBuilder for footer padding
|
||||||
func (f *FooterConfigBuilder) Padding() *FooterPaddingBuilder {
|
func (f *FooterConfigBuilder) Padding() *FooterPaddingBuilder {
|
||||||
return &FooterPaddingBuilder{
|
return &FooterPaddingBuilder{
|
||||||
|
|
@ -478,9 +502,10 @@ func (hf *HeaderFormattingBuilder) WithAutoWrap(autoWrap int) *HeaderFormattingB
|
||||||
return hf
|
return hf
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithMergeMode sets merge mode
|
// Deprecated: Use .CellMerging().WithMode(...) instead. This method will be removed in a future version.
|
||||||
func (hf *HeaderFormattingBuilder) WithMergeMode(mergeMode int) *HeaderFormattingBuilder {
|
func (hf *HeaderFormattingBuilder) WithMergeMode(mergeMode int) *HeaderFormattingBuilder {
|
||||||
if mergeMode >= tw.MergeNone && mergeMode <= tw.MergeHierarchical {
|
if mergeMode >= tw.MergeNone && mergeMode <= tw.MergeHierarchical {
|
||||||
|
hf.parent.config.Merging.Mode = mergeMode
|
||||||
hf.config.MergeMode = mergeMode
|
hf.config.MergeMode = mergeMode
|
||||||
}
|
}
|
||||||
return hf
|
return hf
|
||||||
|
|
@ -512,9 +537,10 @@ func (rf *RowFormattingBuilder) WithAutoWrap(autoWrap int) *RowFormattingBuilder
|
||||||
return rf
|
return rf
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithMergeMode sets merge mode
|
// Deprecated: Use .CellMerging().WithMode(...) instead. This method will be removed in a future version.
|
||||||
func (rf *RowFormattingBuilder) WithMergeMode(mergeMode int) *RowFormattingBuilder {
|
func (rf *RowFormattingBuilder) WithMergeMode(mergeMode int) *RowFormattingBuilder {
|
||||||
if mergeMode >= tw.MergeNone && mergeMode <= tw.MergeHierarchical {
|
if mergeMode >= tw.MergeNone && mergeMode <= tw.MergeHierarchical {
|
||||||
|
rf.parent.config.Merging.Mode = mergeMode
|
||||||
rf.config.MergeMode = mergeMode
|
rf.config.MergeMode = mergeMode
|
||||||
}
|
}
|
||||||
return rf
|
return rf
|
||||||
|
|
@ -546,14 +572,117 @@ func (ff *FooterFormattingBuilder) WithAutoWrap(autoWrap int) *FooterFormattingB
|
||||||
return ff
|
return ff
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithMergeMode sets merge mode
|
// Deprecated: Use .CellMerging().WithMode(...) instead. This method will be removed in a future version.
|
||||||
func (ff *FooterFormattingBuilder) WithMergeMode(mergeMode int) *FooterFormattingBuilder {
|
func (ff *FooterFormattingBuilder) WithMergeMode(mergeMode int) *FooterFormattingBuilder {
|
||||||
if mergeMode >= tw.MergeNone && mergeMode <= tw.MergeHierarchical {
|
if mergeMode >= tw.MergeNone && mergeMode <= tw.MergeHierarchical {
|
||||||
|
ff.parent.config.Merging.Mode = mergeMode
|
||||||
ff.config.MergeMode = mergeMode
|
ff.config.MergeMode = mergeMode
|
||||||
}
|
}
|
||||||
return ff
|
return ff
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HeaderMergingBuilder configures header cell merging
|
||||||
|
type HeaderMergingBuilder struct {
|
||||||
|
parent *HeaderConfigBuilder
|
||||||
|
config *tw.CellMerging
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build returns the parent HeaderConfigBuilder.
|
||||||
|
func (hm *HeaderMergingBuilder) Build() *HeaderConfigBuilder {
|
||||||
|
return hm.parent
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMode sets the merge mode (e.g., tw.MergeHorizontal).
|
||||||
|
func (hm *HeaderMergingBuilder) WithMode(mode int) *HeaderMergingBuilder {
|
||||||
|
hm.config.Mode = mode
|
||||||
|
// Also set the deprecated field for backward compatibility
|
||||||
|
hm.parent.config.Formatting.MergeMode = mode
|
||||||
|
return hm
|
||||||
|
}
|
||||||
|
|
||||||
|
// ByColumnIndex sets specific columns to be merged by their index.
|
||||||
|
// If not called, merging applies to all columns.
|
||||||
|
func (hm *HeaderMergingBuilder) ByColumnIndex(indices []int) *HeaderMergingBuilder {
|
||||||
|
if len(indices) == 0 {
|
||||||
|
hm.config.ByColumnIndex = nil // nil means apply to all
|
||||||
|
} else {
|
||||||
|
mapper := tw.NewMapper[int, bool]()
|
||||||
|
for _, idx := range indices {
|
||||||
|
mapper.Set(idx, true)
|
||||||
|
}
|
||||||
|
hm.config.ByColumnIndex = mapper
|
||||||
|
}
|
||||||
|
return hm
|
||||||
|
}
|
||||||
|
|
||||||
|
// RowMergingBuilder configures row cell merging
|
||||||
|
type RowMergingBuilder struct {
|
||||||
|
parent *RowConfigBuilder
|
||||||
|
config *tw.CellMerging
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build returns the parent RowConfigBuilder.
|
||||||
|
func (rm *RowMergingBuilder) Build() *RowConfigBuilder {
|
||||||
|
return rm.parent
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMode sets the merge mode (e.g., tw.MergeVertical, tw.MergeHierarchical).
|
||||||
|
func (rm *RowMergingBuilder) WithMode(mode int) *RowMergingBuilder {
|
||||||
|
rm.config.Mode = mode
|
||||||
|
// Also set the deprecated field for backward compatibility
|
||||||
|
rm.parent.config.Formatting.MergeMode = mode
|
||||||
|
return rm
|
||||||
|
}
|
||||||
|
|
||||||
|
// ByColumnIndex sets specific columns to be merged by their index.
|
||||||
|
// If not called, merging applies to all columns.
|
||||||
|
func (rm *RowMergingBuilder) ByColumnIndex(indices []int) *RowMergingBuilder {
|
||||||
|
if len(indices) == 0 {
|
||||||
|
rm.config.ByColumnIndex = nil // nil means apply to all
|
||||||
|
} else {
|
||||||
|
mapper := tw.NewMapper[int, bool]()
|
||||||
|
for _, idx := range indices {
|
||||||
|
mapper.Set(idx, true)
|
||||||
|
}
|
||||||
|
rm.config.ByColumnIndex = mapper
|
||||||
|
}
|
||||||
|
return rm
|
||||||
|
}
|
||||||
|
|
||||||
|
// FooterMergingBuilder configures footer cell merging
|
||||||
|
type FooterMergingBuilder struct {
|
||||||
|
parent *FooterConfigBuilder
|
||||||
|
config *tw.CellMerging
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build returns the parent FooterConfigBuilder.
|
||||||
|
func (fm *FooterMergingBuilder) Build() *FooterConfigBuilder {
|
||||||
|
return fm.parent
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMode sets the merge mode (e.g., tw.MergeHorizontal).
|
||||||
|
func (fm *FooterMergingBuilder) WithMode(mode int) *FooterMergingBuilder {
|
||||||
|
fm.config.Mode = mode
|
||||||
|
// Also set the deprecated field for backward compatibility
|
||||||
|
fm.parent.config.Formatting.MergeMode = mode
|
||||||
|
return fm
|
||||||
|
}
|
||||||
|
|
||||||
|
// ByColumnIndex sets specific columns to be merged by their index.
|
||||||
|
// If not called, merging applies to all columns.
|
||||||
|
func (fm *FooterMergingBuilder) ByColumnIndex(indices []int) *FooterMergingBuilder {
|
||||||
|
if len(indices) == 0 {
|
||||||
|
fm.config.ByColumnIndex = nil // nil means apply to all
|
||||||
|
} else {
|
||||||
|
mapper := tw.NewMapper[int, bool]()
|
||||||
|
for _, idx := range indices {
|
||||||
|
mapper.Set(idx, true)
|
||||||
|
}
|
||||||
|
fm.config.ByColumnIndex = mapper
|
||||||
|
}
|
||||||
|
return fm
|
||||||
|
}
|
||||||
|
|
||||||
// HeaderPaddingBuilder configures header padding
|
// HeaderPaddingBuilder configures header padding
|
||||||
type HeaderPaddingBuilder struct {
|
type HeaderPaddingBuilder struct {
|
||||||
parent *HeaderConfigBuilder
|
parent *HeaderConfigBuilder
|
||||||
|
|
|
||||||
15
vendor/github.com/olekukonko/tablewriter/deprecated.go
generated
vendored
15
vendor/github.com/olekukonko/tablewriter/deprecated.go
generated
vendored
|
|
@ -1,6 +1,8 @@
|
||||||
package tablewriter
|
package tablewriter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/mattn/go-runewidth"
|
||||||
|
"github.com/olekukonko/tablewriter/pkg/twwidth"
|
||||||
"github.com/olekukonko/tablewriter/tw"
|
"github.com/olekukonko/tablewriter/tw"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -218,3 +220,16 @@ 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
248
vendor/github.com/olekukonko/tablewriter/new.txt
generated
vendored
Normal file
248
vendor/github.com/olekukonko/tablewriter/new.txt
generated
vendored
Normal file
|
|
@ -0,0 +1,248 @@
|
||||||
|
PASS
|
||||||
|
ok github.com/olekukonko/tablewriter 0.284s
|
||||||
|
? github.com/olekukonko/tablewriter/cmd/csv2table [no test files]
|
||||||
|
goos: darwin
|
||||||
|
goarch: arm64
|
||||||
|
pkg: github.com/olekukonko/tablewriter/pkg/twwarp
|
||||||
|
cpu: Apple M2
|
||||||
|
BenchmarkWrapString-8 10030 114909 ns/op 87.40 MB/s 48488 B/op 33 allocs/op
|
||||||
|
BenchmarkWrapString-8 10000 112188 ns/op 89.52 MB/s 48488 B/op 33 allocs/op
|
||||||
|
BenchmarkWrapString-8 10000 113708 ns/op 88.32 MB/s 48488 B/op 33 allocs/op
|
||||||
|
BenchmarkWrapString-8 10000 113233 ns/op 88.69 MB/s 48488 B/op 33 allocs/op
|
||||||
|
BenchmarkWrapString-8 10000 112575 ns/op 89.21 MB/s 48488 B/op 33 allocs/op
|
||||||
|
BenchmarkWrapString-8 10000 112604 ns/op 89.19 MB/s 48488 B/op 33 allocs/op
|
||||||
|
BenchmarkWrapStringWithSpaces-8 10000 113731 ns/op 88.30 MB/s 54024 B/op 51 allocs/op
|
||||||
|
BenchmarkWrapStringWithSpaces-8 10000 113511 ns/op 88.48 MB/s 54024 B/op 51 allocs/op
|
||||||
|
BenchmarkWrapStringWithSpaces-8 10000 113575 ns/op 88.43 MB/s 54024 B/op 51 allocs/op
|
||||||
|
BenchmarkWrapStringWithSpaces-8 10000 113746 ns/op 88.29 MB/s 54024 B/op 51 allocs/op
|
||||||
|
BenchmarkWrapStringWithSpaces-8 10000 113473 ns/op 88.51 MB/s 54024 B/op 51 allocs/op
|
||||||
|
BenchmarkWrapStringWithSpaces-8 10000 114487 ns/op 87.72 MB/s 54024 B/op 51 allocs/op
|
||||||
|
PASS
|
||||||
|
ok github.com/olekukonko/tablewriter/pkg/twwarp 14.612s
|
||||||
|
goos: darwin
|
||||||
|
goarch: arm64
|
||||||
|
pkg: github.com/olekukonko/tablewriter/pkg/twwidth
|
||||||
|
cpu: Apple M2
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_NoCache-8 264374 4533 ns/op 119.12 MB/s 1178 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_NoCache-8 265746 4514 ns/op 119.62 MB/s 1177 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_NoCache-8 263538 4509 ns/op 119.75 MB/s 1178 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_NoCache-8 266173 4510 ns/op 119.72 MB/s 1180 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_NoCache-8 265224 4676 ns/op 115.48 MB/s 1180 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_NoCache-8 265696 4508 ns/op 119.80 MB/s 1177 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheMiss-8 251047 4859 ns/op 111.13 MB/s 1867 B/op 4 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheMiss-8 1000000 3945 ns/op 136.89 MB/s 1584 B/op 4 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheMiss-8 3504475 3729 ns/op 144.81 MB/s 1474 B/op 4 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheMiss-8 3664098 635.4 ns/op 849.84 MB/s 670 B/op 2 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheMiss-8 3818680 588.6 ns/op 917.47 MB/s 667 B/op 2 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheMiss-8 3761966 348.7 ns/op 1548.66 MB/s 583 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheHit-8 49524442 23.54 ns/op 22938.55 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheHit-8 51765230 23.25 ns/op 23221.81 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheHit-8 51881983 23.83 ns/op 22664.79 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheHit-8 51665586 23.20 ns/op 23272.39 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheHit-8 51782077 23.23 ns/op 23250.20 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheHit-8 51498277 23.21 ns/op 23267.21 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_NoCache-8 263586 4520 ns/op 119.47 MB/s 1183 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_NoCache-8 265484 4519 ns/op 119.49 MB/s 1182 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_NoCache-8 265218 4514 ns/op 119.64 MB/s 1181 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_NoCache-8 265957 4515 ns/op 119.60 MB/s 1184 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_NoCache-8 265981 4518 ns/op 119.52 MB/s 1183 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_NoCache-8 265028 4574 ns/op 118.06 MB/s 1184 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheMiss-8 251682 4853 ns/op 111.27 MB/s 1869 B/op 4 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheMiss-8 1000000 3893 ns/op 138.70 MB/s 1583 B/op 4 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheMiss-8 3596130 3747 ns/op 144.13 MB/s 1499 B/op 4 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheMiss-8 3671358 506.1 ns/op 1066.92 MB/s 628 B/op 2 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheMiss-8 3687993 370.6 ns/op 1456.96 MB/s 594 B/op 2 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheMiss-8 3672946 358.4 ns/op 1506.88 MB/s 583 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheHit-8 49266897 23.64 ns/op 22844.78 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheHit-8 50158659 23.54 ns/op 22938.83 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheHit-8 50689321 23.45 ns/op 23025.77 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheHit-8 51113672 23.52 ns/op 22954.95 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheHit-8 51489162 23.21 ns/op 23269.51 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheHit-8 51705564 23.16 ns/op 23311.21 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_NoCache-8 20930 57159 ns/op 35.86 MB/s 1389 B/op 9 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_NoCache-8 20882 57502 ns/op 35.65 MB/s 1395 B/op 9 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_NoCache-8 21103 57730 ns/op 35.51 MB/s 1391 B/op 9 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_NoCache-8 20889 56615 ns/op 36.21 MB/s 1393 B/op 9 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_NoCache-8 20808 58303 ns/op 35.16 MB/s 1391 B/op 9 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_NoCache-8 21104 56727 ns/op 36.14 MB/s 1387 B/op 9 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheMiss-8 38569 27485 ns/op 74.59 MB/s 3041 B/op 6 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheMiss-8 1000000 58061 ns/op 35.31 MB/s 3835 B/op 10 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheMiss-8 2124566 31025 ns/op 66.08 MB/s 3140 B/op 6 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheMiss-8 1000000 1607 ns/op 1275.74 MB/s 2311 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheMiss-8 1615826 1224 ns/op 1674.27 MB/s 2311 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheMiss-8 1478348 722.9 ns/op 2835.84 MB/s 2311 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheHit-8 23989044 44.26 ns/op 46313.25 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheHit-8 27268802 44.13 ns/op 46454.64 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheHit-8 27292006 44.51 ns/op 46054.40 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheHit-8 24128786 44.99 ns/op 45569.06 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheHit-8 26858004 44.09 ns/op 46497.43 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheHit-8 27259458 44.05 ns/op 46538.64 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_NoCache-8 20671 57887 ns/op 35.41 MB/s 1395 B/op 9 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_NoCache-8 20966 56795 ns/op 36.09 MB/s 1396 B/op 9 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_NoCache-8 20708 57092 ns/op 35.91 MB/s 1388 B/op 9 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_NoCache-8 20882 57917 ns/op 35.40 MB/s 1389 B/op 9 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_NoCache-8 21244 58013 ns/op 35.34 MB/s 1393 B/op 9 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_NoCache-8 20854 58122 ns/op 35.27 MB/s 1396 B/op 9 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheMiss-8 38907 30289 ns/op 67.68 MB/s 3066 B/op 6 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheMiss-8 1000000 56603 ns/op 36.22 MB/s 3835 B/op 10 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheMiss-8 1949059 29030 ns/op 70.62 MB/s 3084 B/op 6 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheMiss-8 1479127 933.7 ns/op 2195.47 MB/s 2311 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheMiss-8 2335996 11012 ns/op 186.17 MB/s 2548 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheMiss-8 983864 1169 ns/op 1753.75 MB/s 2311 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheHit-8 27291516 44.18 ns/op 46398.32 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheHit-8 27220657 44.18 ns/op 46402.04 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheHit-8 27059124 44.91 ns/op 45645.46 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheHit-8 26679783 44.04 ns/op 46551.62 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheHit-8 27244114 44.14 ns/op 46448.19 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheHit-8 27221737 44.61 ns/op 45948.75 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_NoCache-8 3247359 366.1 ns/op 95.62 MB/s 113 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_NoCache-8 3292773 370.6 ns/op 94.44 MB/s 113 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_NoCache-8 3275070 365.3 ns/op 95.82 MB/s 113 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_NoCache-8 3291489 365.6 ns/op 95.73 MB/s 113 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_NoCache-8 3282121 374.9 ns/op 93.37 MB/s 113 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_NoCache-8 3198205 375.6 ns/op 93.18 MB/s 113 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheMiss-8 3092488 419.4 ns/op 83.45 MB/s 152 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheMiss-8 6276060 476.4 ns/op 73.46 MB/s 166 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheMiss-8 6135336 218.8 ns/op 159.98 MB/s 55 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheMiss-8 6175833 216.1 ns/op 161.95 MB/s 55 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheMiss-8 6156606 215.2 ns/op 162.63 MB/s 55 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheMiss-8 6160923 216.2 ns/op 161.88 MB/s 55 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheHit-8 78655855 15.02 ns/op 2330.76 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheHit-8 70905223 14.59 ns/op 2398.68 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheHit-8 82255629 14.49 ns/op 2415.75 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheHit-8 82383864 14.48 ns/op 2417.21 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheHit-8 82325931 14.49 ns/op 2415.73 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheHit-8 82426311 14.66 ns/op 2386.73 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_NoCache-8 3265182 365.8 ns/op 95.68 MB/s 113 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_NoCache-8 3275419 366.3 ns/op 95.56 MB/s 113 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_NoCache-8 3057087 375.3 ns/op 93.26 MB/s 113 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_NoCache-8 3239217 372.6 ns/op 93.94 MB/s 113 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_NoCache-8 3246429 367.3 ns/op 95.29 MB/s 113 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_NoCache-8 3252763 365.3 ns/op 95.80 MB/s 113 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheMiss-8 2986195 396.4 ns/op 88.30 MB/s 142 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheMiss-8 6487422 493.6 ns/op 70.90 MB/s 168 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheMiss-8 6261225 216.1 ns/op 161.99 MB/s 55 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheMiss-8 6154988 210.7 ns/op 166.13 MB/s 55 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheMiss-8 6308702 213.8 ns/op 163.69 MB/s 55 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheMiss-8 6120438 216.0 ns/op 162.05 MB/s 55 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheHit-8 82184980 14.47 ns/op 2419.17 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheHit-8 78985473 14.51 ns/op 2412.95 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheHit-8 82368319 14.47 ns/op 2419.30 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheHit-8 82366668 14.47 ns/op 2418.96 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheHit-8 82104614 14.53 ns/op 2409.59 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheHit-8 82399426 14.53 ns/op 2409.13 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_NoCache-8 1000000 1020 ns/op 59.80 MB/s 186 B/op 6 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_NoCache-8 1000000 1010 ns/op 60.40 MB/s 185 B/op 6 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_NoCache-8 1000000 1007 ns/op 60.55 MB/s 186 B/op 6 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_NoCache-8 1000000 1006 ns/op 60.63 MB/s 185 B/op 6 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_NoCache-8 1000000 1006 ns/op 60.65 MB/s 185 B/op 6 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_NoCache-8 1000000 1006 ns/op 60.63 MB/s 185 B/op 6 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheMiss-8 1000000 1334 ns/op 45.74 MB/s 436 B/op 7 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheMiss-8 6892693 1204 ns/op 50.65 MB/s 321 B/op 7 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheMiss-8 6433399 221.7 ns/op 275.14 MB/s 87 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheMiss-8 6323521 221.2 ns/op 275.73 MB/s 87 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheMiss-8 6000822 218.5 ns/op 279.15 MB/s 87 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheMiss-8 6329578 220.3 ns/op 276.90 MB/s 87 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheHit-8 80806719 14.65 ns/op 4163.13 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheHit-8 82397774 14.63 ns/op 4169.11 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheHit-8 82794307 14.76 ns/op 4134.15 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheHit-8 82610730 14.59 ns/op 4180.13 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheHit-8 82639170 14.58 ns/op 4183.56 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheHit-8 82560049 14.45 ns/op 4222.53 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_NoCache-8 1000000 1006 ns/op 60.61 MB/s 185 B/op 6 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_NoCache-8 1000000 1012 ns/op 60.29 MB/s 185 B/op 6 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_NoCache-8 1000000 1030 ns/op 59.25 MB/s 185 B/op 6 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_NoCache-8 1000000 1005 ns/op 60.68 MB/s 185 B/op 6 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_NoCache-8 1000000 1006 ns/op 60.64 MB/s 186 B/op 6 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_NoCache-8 1000000 1012 ns/op 60.26 MB/s 185 B/op 6 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheMiss-8 1000000 1361 ns/op 44.84 MB/s 436 B/op 7 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheMiss-8 6967185 1216 ns/op 50.17 MB/s 323 B/op 7 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheMiss-8 6413974 219.1 ns/op 278.46 MB/s 87 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheMiss-8 6381684 216.9 ns/op 281.27 MB/s 87 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheMiss-8 6383749 216.2 ns/op 282.14 MB/s 87 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheMiss-8 6360810 217.3 ns/op 280.75 MB/s 87 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheHit-8 81573231 14.53 ns/op 4197.28 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheHit-8 82780268 14.47 ns/op 4215.84 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheHit-8 82845276 14.48 ns/op 4212.74 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheHit-8 82545850 14.51 ns/op 4203.96 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheHit-8 82419704 14.49 ns/op 4209.69 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheHit-8 82121707 14.50 ns/op 4206.82 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_NoCache-8 3552715 336.1 ns/op 178.50 MB/s 146 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_NoCache-8 3551234 335.0 ns/op 179.09 MB/s 146 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_NoCache-8 3588946 338.9 ns/op 177.05 MB/s 146 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_NoCache-8 3577424 338.5 ns/op 177.25 MB/s 146 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_NoCache-8 3554505 335.4 ns/op 178.89 MB/s 146 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_NoCache-8 3575703 336.2 ns/op 178.46 MB/s 146 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_CacheMiss-8 2990224 412.6 ns/op 145.42 MB/s 207 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_CacheMiss-8 6066997 484.0 ns/op 123.95 MB/s 232 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_CacheMiss-8 5743347 224.3 ns/op 267.49 MB/s 87 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_CacheMiss-8 5870154 220.6 ns/op 271.92 MB/s 87 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_CacheMiss-8 5880489 228.0 ns/op 263.14 MB/s 87 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_CacheMiss-8 5660132 226.8 ns/op 264.52 MB/s 87 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_CacheHit-8 81708613 14.54 ns/op 4126.40 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_CacheHit-8 79903231 14.65 ns/op 4094.56 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_CacheHit-8 80580853 14.62 ns/op 4103.14 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_CacheHit-8 82036092 14.73 ns/op 4073.52 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_CacheHit-8 83622964 14.49 ns/op 4139.65 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_CacheHit-8 82724623 14.53 ns/op 4129.78 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_NoCache-8 3463408 349.4 ns/op 171.71 MB/s 145 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_NoCache-8 3245782 350.0 ns/op 171.41 MB/s 146 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_NoCache-8 3461160 348.3 ns/op 172.28 MB/s 146 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_NoCache-8 3453544 349.1 ns/op 171.87 MB/s 146 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_NoCache-8 3443858 347.0 ns/op 172.92 MB/s 146 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_NoCache-8 3469286 347.4 ns/op 172.72 MB/s 146 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_CacheMiss-8 3050086 428.5 ns/op 140.04 MB/s 213 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_CacheMiss-8 5927800 476.0 ns/op 126.05 MB/s 230 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_CacheMiss-8 5852149 223.0 ns/op 269.05 MB/s 87 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_CacheMiss-8 5721747 224.9 ns/op 266.80 MB/s 87 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_CacheMiss-8 5751147 225.7 ns/op 265.84 MB/s 87 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_CacheMiss-8 5893626 225.9 ns/op 265.55 MB/s 87 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_CacheHit-8 81984477 14.52 ns/op 4132.81 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_CacheHit-8 79537578 14.59 ns/op 4112.59 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_CacheHit-8 82339353 14.56 ns/op 4119.49 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_CacheHit-8 82286889 14.92 ns/op 4020.68 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_CacheHit-8 82166224 14.53 ns/op 4129.14 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_CacheHit-8 83084276 14.52 ns/op 4131.45 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_NoCache-8 1221180 982.5 ns/op 88.55 MB/s 193 B/op 5 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_NoCache-8 1210902 983.5 ns/op 88.46 MB/s 193 B/op 5 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_NoCache-8 1223528 989.3 ns/op 87.94 MB/s 193 B/op 5 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_NoCache-8 1212517 984.1 ns/op 88.40 MB/s 193 B/op 5 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_NoCache-8 1224182 983.5 ns/op 88.46 MB/s 193 B/op 5 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_NoCache-8 1000000 1007 ns/op 86.36 MB/s 193 B/op 5 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheMiss-8 999058 1364 ns/op 63.76 MB/s 603 B/op 7 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheMiss-8 6682279 1218 ns/op 71.40 MB/s 465 B/op 7 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheMiss-8 6339568 220.6 ns/op 394.46 MB/s 103 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheMiss-8 6226921 222.3 ns/op 391.34 MB/s 103 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheMiss-8 6264051 221.1 ns/op 393.47 MB/s 103 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheMiss-8 6234439 222.4 ns/op 391.23 MB/s 103 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheHit-8 75337251 15.64 ns/op 5562.01 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheHit-8 76826634 15.76 ns/op 5521.54 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheHit-8 76836674 15.79 ns/op 5508.81 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheHit-8 76840162 15.64 ns/op 5564.05 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheHit-8 76694060 15.60 ns/op 5577.81 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheHit-8 76737175 15.62 ns/op 5571.56 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_NoCache-8 1202406 1012 ns/op 85.93 MB/s 193 B/op 5 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_NoCache-8 1000000 1000 ns/op 86.99 MB/s 193 B/op 5 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_NoCache-8 1208559 993.7 ns/op 87.55 MB/s 193 B/op 5 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_NoCache-8 1209415 990.9 ns/op 87.80 MB/s 193 B/op 5 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_NoCache-8 1206118 1020 ns/op 85.33 MB/s 193 B/op 5 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_NoCache-8 1211994 990.6 ns/op 87.82 MB/s 194 B/op 5 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheMiss-8 1000000 1363 ns/op 63.84 MB/s 603 B/op 7 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheMiss-8 6504960 1214 ns/op 71.65 MB/s 465 B/op 7 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheMiss-8 6349030 220.2 ns/op 395.18 MB/s 103 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheMiss-8 6183368 220.3 ns/op 394.99 MB/s 103 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheMiss-8 6240484 220.6 ns/op 394.32 MB/s 103 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheMiss-8 6280713 222.0 ns/op 391.95 MB/s 103 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheHit-8 69630140 15.77 ns/op 5517.31 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheHit-8 76043014 15.65 ns/op 5559.61 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheHit-8 76239080 15.63 ns/op 5567.94 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheHit-8 75864739 15.88 ns/op 5479.13 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheHit-8 71286422 15.74 ns/op 5527.29 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheHit-8 75704404 15.71 ns/op 5536.58 MB/s 0 B/op 0 allocs/op
|
||||||
|
PASS
|
||||||
|
ok github.com/olekukonko/tablewriter/pkg/twwidth 659.150s
|
||||||
|
? github.com/olekukonko/tablewriter/renderer [no test files]
|
||||||
|
PASS
|
||||||
|
ok github.com/olekukonko/tablewriter/tests 3.025s
|
||||||
|
PASS
|
||||||
|
ok github.com/olekukonko/tablewriter/tw 0.283s
|
||||||
248
vendor/github.com/olekukonko/tablewriter/old.txt
generated
vendored
Normal file
248
vendor/github.com/olekukonko/tablewriter/old.txt
generated
vendored
Normal file
|
|
@ -0,0 +1,248 @@
|
||||||
|
PASS
|
||||||
|
ok github.com/olekukonko/tablewriter 0.819s
|
||||||
|
? github.com/olekukonko/tablewriter/cmd/csv2table [no test files]
|
||||||
|
goos: darwin
|
||||||
|
goarch: arm64
|
||||||
|
pkg: github.com/olekukonko/tablewriter/pkg/twwarp
|
||||||
|
cpu: Apple M2
|
||||||
|
BenchmarkWrapString-8 10630 111320 ns/op 90.22 MB/s 48488 B/op 33 allocs/op
|
||||||
|
BenchmarkWrapString-8 10000 112981 ns/op 88.89 MB/s 48488 B/op 33 allocs/op
|
||||||
|
BenchmarkWrapString-8 10000 113419 ns/op 88.55 MB/s 48488 B/op 33 allocs/op
|
||||||
|
BenchmarkWrapString-8 10000 112794 ns/op 89.04 MB/s 48488 B/op 33 allocs/op
|
||||||
|
BenchmarkWrapString-8 10000 112400 ns/op 89.35 MB/s 48488 B/op 33 allocs/op
|
||||||
|
BenchmarkWrapString-8 10000 112767 ns/op 89.06 MB/s 48488 B/op 33 allocs/op
|
||||||
|
BenchmarkWrapStringWithSpaces-8 10000 115098 ns/op 87.26 MB/s 54024 B/op 51 allocs/op
|
||||||
|
BenchmarkWrapStringWithSpaces-8 10000 113343 ns/op 88.61 MB/s 54024 B/op 51 allocs/op
|
||||||
|
BenchmarkWrapStringWithSpaces-8 10000 113702 ns/op 88.33 MB/s 54024 B/op 51 allocs/op
|
||||||
|
BenchmarkWrapStringWithSpaces-8 10000 113547 ns/op 88.45 MB/s 54024 B/op 51 allocs/op
|
||||||
|
BenchmarkWrapStringWithSpaces-8 10000 113016 ns/op 88.86 MB/s 54024 B/op 51 allocs/op
|
||||||
|
BenchmarkWrapStringWithSpaces-8 10000 113206 ns/op 88.71 MB/s 54024 B/op 51 allocs/op
|
||||||
|
PASS
|
||||||
|
ok github.com/olekukonko/tablewriter/pkg/twwarp 15.179s
|
||||||
|
goos: darwin
|
||||||
|
goarch: arm64
|
||||||
|
pkg: github.com/olekukonko/tablewriter/pkg/twwidth
|
||||||
|
cpu: Apple M2
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_NoCache-8 2953855 387.1 ns/op 90.40 MB/s 112 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_NoCache-8 3095179 387.8 ns/op 90.24 MB/s 112 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_NoCache-8 3096141 391.0 ns/op 89.51 MB/s 113 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_NoCache-8 3090711 387.2 ns/op 90.40 MB/s 113 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_NoCache-8 3066110 387.4 ns/op 90.35 MB/s 112 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_NoCache-8 3098689 389.2 ns/op 89.92 MB/s 112 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheMiss-8 3125685 440.9 ns/op 79.39 MB/s 159 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheMiss-8 6477175 496.2 ns/op 70.53 MB/s 165 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheMiss-8 6019939 217.7 ns/op 160.79 MB/s 55 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheMiss-8 6231590 219.2 ns/op 159.67 MB/s 55 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheMiss-8 6245622 216.2 ns/op 161.90 MB/s 55 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheMiss-8 6109658 218.8 ns/op 159.95 MB/s 55 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheHit-8 80977806 14.73 ns/op 2375.87 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheHit-8 80972566 14.76 ns/op 2371.06 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheHit-8 81432532 14.90 ns/op 2348.78 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheHit-8 80644483 14.85 ns/op 2357.10 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheHit-8 81361905 14.79 ns/op 2365.80 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAfalse_CacheHit-8 81612987 14.78 ns/op 2368.60 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_NoCache-8 1777732 682.2 ns/op 51.30 MB/s 113 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_NoCache-8 1778122 672.9 ns/op 52.01 MB/s 113 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_NoCache-8 1779956 674.0 ns/op 51.93 MB/s 112 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_NoCache-8 1773282 678.7 ns/op 51.57 MB/s 113 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_NoCache-8 1783092 680.2 ns/op 51.46 MB/s 113 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_NoCache-8 1780448 674.0 ns/op 51.93 MB/s 113 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheMiss-8 1000000 1027 ns/op 34.08 MB/s 333 B/op 4 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheMiss-8 6891168 958.3 ns/op 36.52 MB/s 227 B/op 4 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheMiss-8 6165972 211.7 ns/op 165.30 MB/s 55 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheMiss-8 6370098 217.4 ns/op 161.02 MB/s 55 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheMiss-8 6193920 214.8 ns/op 162.92 MB/s 55 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheMiss-8 6190384 209.4 ns/op 167.16 MB/s 55 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheHit-8 79747688 14.75 ns/op 2372.71 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheHit-8 79607492 14.75 ns/op 2372.90 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheHit-8 81634501 14.73 ns/op 2376.30 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheHit-8 81644916 14.70 ns/op 2381.26 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheHit-8 82505884 14.70 ns/op 2380.77 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/SimpleASCII_EAtrue_CacheHit-8 81840265 14.70 ns/op 2380.34 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_NoCache-8 1000000 1053 ns/op 57.95 MB/s 185 B/op 6 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_NoCache-8 1000000 1028 ns/op 59.34 MB/s 185 B/op 6 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_NoCache-8 1000000 1029 ns/op 59.27 MB/s 185 B/op 6 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_NoCache-8 1000000 1025 ns/op 59.49 MB/s 185 B/op 6 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_NoCache-8 1000000 1026 ns/op 59.48 MB/s 185 B/op 6 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_NoCache-8 1000000 1025 ns/op 59.54 MB/s 185 B/op 6 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheMiss-8 1000000 1352 ns/op 45.13 MB/s 437 B/op 7 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheMiss-8 6619118 1219 ns/op 50.06 MB/s 320 B/op 7 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheMiss-8 6486976 221.2 ns/op 275.81 MB/s 87 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheMiss-8 6508150 217.8 ns/op 280.07 MB/s 87 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheMiss-8 6487533 217.4 ns/op 280.56 MB/s 87 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheMiss-8 6243558 216.4 ns/op 281.93 MB/s 87 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheHit-8 80787679 14.90 ns/op 4093.19 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheHit-8 81640521 14.89 ns/op 4097.92 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheHit-8 81596338 14.71 ns/op 4145.47 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheHit-8 81950889 14.84 ns/op 4111.86 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheHit-8 79321578 14.78 ns/op 4126.88 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAfalse_CacheHit-8 81880058 14.75 ns/op 4134.44 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_NoCache-8 906406 1313 ns/op 46.44 MB/s 185 B/op 6 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_NoCache-8 917503 1313 ns/op 46.46 MB/s 185 B/op 6 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_NoCache-8 915308 1312 ns/op 46.49 MB/s 185 B/op 6 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_NoCache-8 918404 1312 ns/op 46.51 MB/s 185 B/op 6 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_NoCache-8 892551 1338 ns/op 45.58 MB/s 185 B/op 6 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_NoCache-8 915020 1333 ns/op 45.76 MB/s 185 B/op 6 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheMiss-8 791368 1633 ns/op 37.36 MB/s 374 B/op 7 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheMiss-8 2314653 1064 ns/op 57.34 MB/s 265 B/op 5 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheMiss-8 6531552 1198 ns/op 50.94 MB/s 258 B/op 5 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheMiss-8 6629763 242.5 ns/op 251.57 MB/s 90 B/op 2 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheMiss-8 6388215 219.1 ns/op 278.36 MB/s 87 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheMiss-8 6472197 218.6 ns/op 279.09 MB/s 87 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheHit-8 80704821 14.76 ns/op 4132.33 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheHit-8 82628028 14.70 ns/op 4149.56 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheHit-8 81870517 14.70 ns/op 4148.97 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheHit-8 81944124 14.99 ns/op 4068.84 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheHit-8 81918950 14.70 ns/op 4150.75 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/ASCIIWithANSI_EAtrue_CacheHit-8 82547270 14.91 ns/op 4092.20 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_NoCache-8 1604370 749.9 ns/op 80.02 MB/s 145 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_NoCache-8 1610148 749.7 ns/op 80.03 MB/s 145 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_NoCache-8 1585026 744.8 ns/op 80.56 MB/s 145 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_NoCache-8 1615032 749.9 ns/op 80.01 MB/s 145 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_NoCache-8 1614980 743.3 ns/op 80.72 MB/s 145 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_NoCache-8 1609586 741.8 ns/op 80.88 MB/s 145 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_CacheMiss-8 1000000 1095 ns/op 54.77 MB/s 428 B/op 4 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_CacheMiss-8 6214893 995.6 ns/op 60.26 MB/s 316 B/op 4 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_CacheMiss-8 5702408 224.5 ns/op 267.21 MB/s 87 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_CacheMiss-8 5712139 220.2 ns/op 272.50 MB/s 87 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_CacheMiss-8 5783916 228.2 ns/op 262.91 MB/s 87 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_CacheMiss-8 5713358 224.0 ns/op 267.91 MB/s 87 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_CacheHit-8 78757815 14.92 ns/op 4020.51 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_CacheHit-8 81419875 14.79 ns/op 4057.15 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_CacheHit-8 81656493 14.75 ns/op 4068.12 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_CacheHit-8 81522430 14.73 ns/op 4073.37 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_CacheHit-8 81887037 14.70 ns/op 4080.93 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAfalse_CacheHit-8 82019505 14.72 ns/op 4074.99 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_NoCache-8 1241600 965.5 ns/op 62.14 MB/s 145 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_NoCache-8 1243646 964.8 ns/op 62.19 MB/s 145 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_NoCache-8 1243516 968.1 ns/op 61.98 MB/s 144 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_NoCache-8 1241917 965.3 ns/op 62.16 MB/s 145 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_NoCache-8 1242903 985.0 ns/op 60.92 MB/s 145 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_NoCache-8 1223456 964.3 ns/op 62.22 MB/s 145 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_CacheMiss-8 1000000 1378 ns/op 43.55 MB/s 428 B/op 4 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_CacheMiss-8 6265657 1229 ns/op 48.84 MB/s 316 B/op 4 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_CacheMiss-8 5960497 224.3 ns/op 267.52 MB/s 87 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_CacheMiss-8 5961004 222.6 ns/op 269.52 MB/s 87 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_CacheMiss-8 5772004 226.5 ns/op 264.87 MB/s 87 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_CacheMiss-8 5766748 223.5 ns/op 268.51 MB/s 87 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_CacheHit-8 78664455 14.76 ns/op 4063.92 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_CacheHit-8 81305858 14.71 ns/op 4079.19 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_CacheHit-8 81626406 14.71 ns/op 4078.32 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_CacheHit-8 81168830 14.71 ns/op 4077.52 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_CacheHit-8 81860040 14.72 ns/op 4075.37 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsian_EAtrue_CacheHit-8 81093633 14.88 ns/op 4031.15 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_NoCache-8 837949 1397 ns/op 62.29 MB/s 193 B/op 5 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_NoCache-8 869082 1380 ns/op 63.04 MB/s 193 B/op 5 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_NoCache-8 864015 1377 ns/op 63.18 MB/s 193 B/op 5 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_NoCache-8 873742 1374 ns/op 63.33 MB/s 193 B/op 5 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_NoCache-8 875703 1375 ns/op 63.27 MB/s 193 B/op 5 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_NoCache-8 866865 1375 ns/op 63.26 MB/s 194 B/op 5 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheMiss-8 772100 1709 ns/op 50.91 MB/s 543 B/op 7 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheMiss-8 2127564 1046 ns/op 83.14 MB/s 361 B/op 5 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheMiss-8 6476034 1274 ns/op 68.30 MB/s 381 B/op 6 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheMiss-8 6401709 221.3 ns/op 393.18 MB/s 103 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheMiss-8 6368766 220.2 ns/op 395.14 MB/s 103 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheMiss-8 6404850 220.6 ns/op 394.34 MB/s 103 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheHit-8 74606566 15.83 ns/op 5494.39 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheHit-8 76326774 15.72 ns/op 5536.01 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheHit-8 76140116 15.74 ns/op 5525.94 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheHit-8 76340330 15.69 ns/op 5544.89 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheHit-8 76240900 15.69 ns/op 5544.81 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAfalse_CacheHit-8 76301294 15.73 ns/op 5531.49 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_NoCache-8 753624 1592 ns/op 54.64 MB/s 193 B/op 5 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_NoCache-8 757292 1599 ns/op 54.42 MB/s 193 B/op 5 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_NoCache-8 758196 1588 ns/op 54.79 MB/s 193 B/op 5 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_NoCache-8 753902 1586 ns/op 54.85 MB/s 193 B/op 5 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_NoCache-8 758770 1589 ns/op 54.74 MB/s 193 B/op 5 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_NoCache-8 757748 1590 ns/op 54.71 MB/s 193 B/op 5 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheMiss-8 653979 1985 ns/op 43.82 MB/s 561 B/op 7 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheMiss-8 2344717 731.5 ns/op 118.93 MB/s 263 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheMiss-8 6440574 1420 ns/op 61.26 MB/s 369 B/op 5 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheMiss-8 6506366 238.2 ns/op 365.22 MB/s 107 B/op 2 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheMiss-8 6504939 220.8 ns/op 394.05 MB/s 103 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheMiss-8 6399746 221.0 ns/op 393.66 MB/s 103 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheHit-8 75646941 15.95 ns/op 5453.57 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheHit-8 75406885 15.73 ns/op 5532.42 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheHit-8 76186243 15.69 ns/op 5545.76 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheHit-8 76350855 15.76 ns/op 5521.29 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheHit-8 76240896 15.70 ns/op 5542.36 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/EastAsianWithANSI_EAtrue_CacheHit-8 76404126 15.90 ns/op 5471.17 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_NoCache-8 241440 4945 ns/op 109.19 MB/s 1181 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_NoCache-8 245013 5050 ns/op 106.94 MB/s 1180 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_NoCache-8 245098 4887 ns/op 110.49 MB/s 1177 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_NoCache-8 244785 4971 ns/op 108.62 MB/s 1179 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_NoCache-8 245007 4880 ns/op 110.66 MB/s 1182 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_NoCache-8 245986 4878 ns/op 110.71 MB/s 1181 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheMiss-8 232534 5203 ns/op 103.78 MB/s 1845 B/op 4 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheMiss-8 1000000 4309 ns/op 125.31 MB/s 1613 B/op 4 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheMiss-8 3491629 4013 ns/op 134.57 MB/s 1471 B/op 4 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheMiss-8 3670467 847.5 ns/op 637.15 MB/s 680 B/op 2 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheMiss-8 3669694 385.0 ns/op 1402.66 MB/s 583 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheMiss-8 3242532 356.5 ns/op 1514.63 MB/s 583 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheHit-8 50391319 23.77 ns/op 22714.54 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheHit-8 51225590 23.32 ns/op 23159.25 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheHit-8 51732408 23.74 ns/op 22751.21 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheHit-8 46074986 24.16 ns/op 22352.67 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheHit-8 43649127 24.43 ns/op 22104.61 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAfalse_CacheHit-8 49954903 23.53 ns/op 22952.45 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_NoCache-8 127574 9378 ns/op 57.58 MB/s 1180 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_NoCache-8 128386 9386 ns/op 57.53 MB/s 1183 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_NoCache-8 128604 9280 ns/op 58.19 MB/s 1178 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_NoCache-8 129218 9264 ns/op 58.29 MB/s 1179 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_NoCache-8 129030 9261 ns/op 58.31 MB/s 1179 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_NoCache-8 129080 9266 ns/op 58.28 MB/s 1180 B/op 3 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheMiss-8 123823 9282 ns/op 58.18 MB/s 1817 B/op 4 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheMiss-8 1000000 8943 ns/op 60.38 MB/s 1754 B/op 4 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheMiss-8 3532728 7337 ns/op 73.60 MB/s 1481 B/op 4 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheMiss-8 3610767 705.9 ns/op 764.94 MB/s 626 B/op 2 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheMiss-8 3502867 387.5 ns/op 1393.73 MB/s 583 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheMiss-8 3706471 680.7 ns/op 793.25 MB/s 640 B/op 2 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheHit-8 51185895 24.01 ns/op 22492.97 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheHit-8 51442992 23.44 ns/op 23041.30 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheHit-8 47312392 23.56 ns/op 22917.72 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheHit-8 51727110 23.33 ns/op 23144.01 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheHit-8 51212746 23.62 ns/op 22862.18 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongSimpleASCII_EAtrue_CacheHit-8 51598200 23.23 ns/op 23247.62 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_NoCache-8 21105 57258 ns/op 35.80 MB/s 1389 B/op 9 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_NoCache-8 20656 57558 ns/op 35.62 MB/s 1386 B/op 9 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_NoCache-8 21045 57257 ns/op 35.80 MB/s 1386 B/op 9 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_NoCache-8 20884 57463 ns/op 35.68 MB/s 1391 B/op 9 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_NoCache-8 20984 56898 ns/op 36.03 MB/s 1388 B/op 9 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_NoCache-8 21164 57796 ns/op 35.47 MB/s 1388 B/op 9 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheMiss-8 103934 31906 ns/op 64.25 MB/s 3143 B/op 6 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheMiss-8 1000000 52097 ns/op 39.35 MB/s 3737 B/op 10 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheMiss-8 1298925 14140 ns/op 144.98 MB/s 2637 B/op 4 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheMiss-8 1000000 1288 ns/op 1592.17 MB/s 2311 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheMiss-8 2546826 30224 ns/op 67.83 MB/s 3071 B/op 6 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheMiss-8 1000000 8376 ns/op 244.74 MB/s 2311 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheHit-8 25786026 44.71 ns/op 45849.62 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheHit-8 27173578 44.15 ns/op 46427.72 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheHit-8 27221428 44.54 ns/op 46030.74 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheHit-8 27213686 44.07 ns/op 46519.79 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheHit-8 27233990 44.27 ns/op 46310.26 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAfalse_CacheHit-8 27164018 44.12 ns/op 46460.92 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_NoCache-8 19785 60051 ns/op 34.14 MB/s 1386 B/op 9 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_NoCache-8 20198 60161 ns/op 34.08 MB/s 1391 B/op 9 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_NoCache-8 19585 60345 ns/op 33.97 MB/s 1390 B/op 9 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_NoCache-8 19956 61714 ns/op 33.22 MB/s 1391 B/op 9 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_NoCache-8 19554 61682 ns/op 33.24 MB/s 1388 B/op 9 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_NoCache-8 19830 60050 ns/op 34.14 MB/s 1393 B/op 9 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheMiss-8 38818 29507 ns/op 69.48 MB/s 3059 B/op 6 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheMiss-8 1000000 58539 ns/op 35.02 MB/s 3835 B/op 10 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheMiss-8 2186653 33757 ns/op 60.73 MB/s 3157 B/op 6 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheMiss-8 1000000 1283 ns/op 1597.72 MB/s 2311 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheMiss-8 1653430 1256 ns/op 1632.67 MB/s 2311 B/op 1 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheMiss-8 2195628 2716 ns/op 754.79 MB/s 2317 B/op 2 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheHit-8 26531894 44.76 ns/op 45801.05 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheHit-8 26634384 44.68 ns/op 45878.57 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheHit-8 27184633 44.97 ns/op 45583.96 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheHit-8 27011893 44.46 ns/op 46104.62 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheHit-8 27183812 44.09 ns/op 46498.94 MB/s 0 B/op 0 allocs/op
|
||||||
|
BenchmarkWidthFunction/LongASCIIWithANSI_EAtrue_CacheHit-8 27269318 44.17 ns/op 46406.38 MB/s 0 B/op 0 allocs/op
|
||||||
|
PASS
|
||||||
|
ok github.com/olekukonko/tablewriter/pkg/twwidth 724.296s
|
||||||
|
? github.com/olekukonko/tablewriter/renderer [no test files]
|
||||||
|
PASS
|
||||||
|
ok github.com/olekukonko/tablewriter/tests 2.959s
|
||||||
|
PASS
|
||||||
|
ok github.com/olekukonko/tablewriter/tw 0.270s
|
||||||
103
vendor/github.com/olekukonko/tablewriter/option.go
generated
vendored
103
vendor/github.com/olekukonko/tablewriter/option.go
generated
vendored
|
|
@ -3,8 +3,8 @@ package tablewriter
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/mattn/go-runewidth"
|
|
||||||
"github.com/olekukonko/ll"
|
"github.com/olekukonko/ll"
|
||||||
|
"github.com/olekukonko/tablewriter/pkg/twcache"
|
||||||
"github.com/olekukonko/tablewriter/pkg/twwidth"
|
"github.com/olekukonko/tablewriter/pkg/twwidth"
|
||||||
"github.com/olekukonko/tablewriter/tw"
|
"github.com/olekukonko/tablewriter/tw"
|
||||||
)
|
)
|
||||||
|
|
@ -122,13 +122,14 @@ func WithFooterAlignmentConfig(alignment tw.CellAlignment) Option {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithFooterMergeMode sets the merge mode for footer cells.
|
// Deprecated: Use a ConfigBuilder with .Footer().CellMerging().WithMode(...) instead.
|
||||||
// Invalid merge modes are ignored, and the change is logged if debugging is enabled.
|
// This option will be removed in a future version.
|
||||||
func WithFooterMergeMode(mergeMode int) Option {
|
func WithFooterMergeMode(mergeMode int) Option {
|
||||||
return func(target *Table) {
|
return func(target *Table) {
|
||||||
if mergeMode < tw.MergeNone || mergeMode > tw.MergeHierarchical {
|
if mergeMode < tw.MergeNone || mergeMode > tw.MergeHierarchical {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
target.config.Footer.Merging.Mode = mergeMode
|
||||||
target.config.Footer.Formatting.MergeMode = mergeMode
|
target.config.Footer.Formatting.MergeMode = mergeMode
|
||||||
if target.logger != nil {
|
if target.logger != nil {
|
||||||
target.logger.Debugf("Option: WithFooterMergeMode applied to Table: %v", mergeMode)
|
target.logger.Debugf("Option: WithFooterMergeMode applied to Table: %v", mergeMode)
|
||||||
|
|
@ -232,13 +233,14 @@ func WithHeaderAutoWrap(wrap int) Option {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithHeaderMergeMode sets the merge mode for header cells.
|
// Deprecated: Use a ConfigBuilder with .Header().CellMerging().WithMode(...) instead.
|
||||||
// Invalid merge modes are ignored, and the change is logged if debugging is enabled.
|
// This option will be removed in a future version.
|
||||||
func WithHeaderMergeMode(mergeMode int) Option {
|
func WithHeaderMergeMode(mergeMode int) Option {
|
||||||
return func(target *Table) {
|
return func(target *Table) {
|
||||||
if mergeMode < tw.MergeNone || mergeMode > tw.MergeHierarchical {
|
if mergeMode < tw.MergeNone || mergeMode > tw.MergeHierarchical {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
target.config.Header.Merging.Mode = mergeMode
|
||||||
target.config.Header.Formatting.MergeMode = mergeMode
|
target.config.Header.Formatting.MergeMode = mergeMode
|
||||||
if target.logger != nil {
|
if target.logger != nil {
|
||||||
target.logger.Debugf("Option: WithHeaderMergeMode applied to Table: %v", mergeMode)
|
target.logger.Debugf("Option: WithHeaderMergeMode applied to Table: %v", mergeMode)
|
||||||
|
|
@ -321,13 +323,14 @@ func WithRowAutoWrap(wrap int) Option {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithRowMergeMode sets the merge mode for row cells.
|
// Deprecated: Use a ConfigBuilder with .Row().CellMerging().WithMode(...) instead.
|
||||||
// Invalid merge modes are ignored, and the change is logged if debugging is enabled.
|
// This option will be removed in a future version.
|
||||||
func WithRowMergeMode(mergeMode int) Option {
|
func WithRowMergeMode(mergeMode int) Option {
|
||||||
return func(target *Table) {
|
return func(target *Table) {
|
||||||
if mergeMode < tw.MergeNone || mergeMode > tw.MergeHierarchical {
|
if mergeMode < tw.MergeNone || mergeMode > tw.MergeHierarchical {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
target.config.Row.Merging.Mode = mergeMode
|
||||||
target.config.Row.Formatting.MergeMode = mergeMode
|
target.config.Row.Formatting.MergeMode = mergeMode
|
||||||
if target.logger != nil {
|
if target.logger != nil {
|
||||||
target.logger.Debugf("Option: WithRowMergeMode applied to Table: %v", mergeMode)
|
target.logger.Debugf("Option: WithRowMergeMode applied to Table: %v", mergeMode)
|
||||||
|
|
@ -468,22 +471,48 @@ func WithStreaming(c tw.StreamConfig) Option {
|
||||||
func WithStringer(stringer interface{}) Option {
|
func WithStringer(stringer interface{}) Option {
|
||||||
return func(t *Table) {
|
return func(t *Table) {
|
||||||
t.stringer = stringer
|
t.stringer = stringer
|
||||||
t.stringerCacheMu.Lock()
|
t.stringerCache = twcache.NewLRU[reflect.Type, reflect.Value](tw.DefaultCacheStringCapacity)
|
||||||
t.stringerCache = make(map[reflect.Type]reflect.Value)
|
|
||||||
t.stringerCacheMu.Unlock()
|
|
||||||
if t.logger != nil {
|
if t.logger != nil {
|
||||||
t.logger.Debug("Stringer updated, cache cleared")
|
t.logger.Debug("Stringer updated, cache cleared")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithStringerCache enables caching for the stringer function.
|
// WithStringerCache enables the default LRU caching for the stringer function.
|
||||||
// Logs the change if debugging is enabled.
|
// It initializes the cache with a default capacity if one does not already exist.
|
||||||
func WithStringerCache() Option {
|
func WithStringerCache() Option {
|
||||||
return func(t *Table) {
|
return func(t *Table) {
|
||||||
t.stringerCacheEnabled = true
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
if t.logger != nil {
|
if t.logger != nil {
|
||||||
t.logger.Debug("Option: WithStringerCache enabled")
|
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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -626,27 +655,20 @@ func WithRendition(rendition tw.Rendition) Option {
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithEastAsian configures the global East Asian width calculation setting.
|
// WithEastAsian configures the global East Asian width calculation setting.
|
||||||
// - enable=true: Enables East Asian width calculations. CJK and ambiguous characters
|
// - state=tw.On: Enables East Asian width calculations. CJK and ambiguous characters
|
||||||
// are typically measured as double width.
|
// are typically measured as double width.
|
||||||
// - enable=false: Disables East Asian width calculations. Characters are generally
|
// - state=tw.Off: Disables East Asian width calculations. Characters are generally
|
||||||
// measured as single width, subject to Unicode standards.
|
// measured as single width, subject to Unicode standards.
|
||||||
//
|
//
|
||||||
// This setting affects all subsequent display width calculations using the twdw package.
|
// This setting affects all subsequent display width calculations using the twdw package.
|
||||||
func WithEastAsian(enable bool) Option {
|
func WithEastAsian(state tw.State) Option {
|
||||||
return func(target *Table) {
|
return func(target *Table) {
|
||||||
twwidth.SetEastAsian(enable)
|
if state.Enabled() {
|
||||||
}
|
twwidth.SetEastAsian(true)
|
||||||
}
|
}
|
||||||
|
if state.Disabled() {
|
||||||
// WithCondition provides a way to set a custom global runewidth.Condition
|
twwidth.SetEastAsian(false)
|
||||||
// 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(condition *runewidth.Condition) Option {
|
|
||||||
return func(target *Table) {
|
|
||||||
twwidth.SetCondition(condition)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -706,6 +728,9 @@ func defaultConfig() Config {
|
||||||
AutoFormat: tw.On,
|
AutoFormat: tw.On,
|
||||||
MergeMode: tw.MergeNone,
|
MergeMode: tw.MergeNone,
|
||||||
},
|
},
|
||||||
|
Merging: tw.CellMerging{
|
||||||
|
Mode: tw.MergeNone,
|
||||||
|
},
|
||||||
Padding: tw.CellPadding{
|
Padding: tw.CellPadding{
|
||||||
Global: tw.PaddingDefault,
|
Global: tw.PaddingDefault,
|
||||||
},
|
},
|
||||||
|
|
@ -720,6 +745,9 @@ func defaultConfig() Config {
|
||||||
AutoFormat: tw.Off,
|
AutoFormat: tw.Off,
|
||||||
MergeMode: tw.MergeNone,
|
MergeMode: tw.MergeNone,
|
||||||
},
|
},
|
||||||
|
Merging: tw.CellMerging{
|
||||||
|
Mode: tw.MergeNone,
|
||||||
|
},
|
||||||
Padding: tw.CellPadding{
|
Padding: tw.CellPadding{
|
||||||
Global: tw.PaddingDefault,
|
Global: tw.PaddingDefault,
|
||||||
},
|
},
|
||||||
|
|
@ -734,6 +762,9 @@ func defaultConfig() Config {
|
||||||
AutoFormat: tw.Off,
|
AutoFormat: tw.Off,
|
||||||
MergeMode: tw.MergeNone,
|
MergeMode: tw.MergeNone,
|
||||||
},
|
},
|
||||||
|
Merging: tw.CellMerging{
|
||||||
|
Mode: tw.MergeNone,
|
||||||
|
},
|
||||||
Padding: tw.CellPadding{
|
Padding: tw.CellPadding{
|
||||||
Global: tw.PaddingDefault,
|
Global: tw.PaddingDefault,
|
||||||
},
|
},
|
||||||
|
|
@ -772,10 +803,20 @@ func mergeCellConfig(dst, src tw.CellConfig) tw.CellConfig {
|
||||||
if src.ColMaxWidths.Global != 0 {
|
if src.ColMaxWidths.Global != 0 {
|
||||||
dst.ColMaxWidths.Global = src.ColMaxWidths.Global
|
dst.ColMaxWidths.Global = src.ColMaxWidths.Global
|
||||||
}
|
}
|
||||||
if src.Formatting.MergeMode != 0 {
|
|
||||||
|
// Handle merging of the new CellMerging struct and the deprecated MergeMode
|
||||||
|
if src.Merging.Mode != 0 {
|
||||||
|
dst.Merging.Mode = src.Merging.Mode
|
||||||
|
dst.Formatting.MergeMode = src.Merging.Mode
|
||||||
|
} else if src.Formatting.MergeMode != 0 {
|
||||||
|
dst.Merging.Mode = src.Formatting.MergeMode
|
||||||
dst.Formatting.MergeMode = src.Formatting.MergeMode
|
dst.Formatting.MergeMode = src.Formatting.MergeMode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if src.Merging.ByColumnIndex != nil {
|
||||||
|
dst.Merging.ByColumnIndex = src.Merging.ByColumnIndex.Clone()
|
||||||
|
}
|
||||||
|
|
||||||
dst.Formatting.AutoFormat = src.Formatting.AutoFormat
|
dst.Formatting.AutoFormat = src.Formatting.AutoFormat
|
||||||
|
|
||||||
if src.Padding.Global.Paddable() {
|
if src.Padding.Global.Paddable() {
|
||||||
|
|
|
||||||
12
vendor/github.com/olekukonko/tablewriter/pkg/twcache/cache.go
generated
vendored
Normal file
12
vendor/github.com/olekukonko/tablewriter/pkg/twcache/cache.go
generated
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
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()
|
||||||
|
}
|
||||||
289
vendor/github.com/olekukonko/tablewriter/pkg/twcache/lru.go
generated
vendored
Normal file
289
vendor/github.com/olekukonko/tablewriter/pkg/twcache/lru.go
generated
vendored
Normal file
|
|
@ -0,0 +1,289 @@
|
||||||
|
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)
|
||||||
|
}
|
||||||
9
vendor/github.com/olekukonko/tablewriter/pkg/twwarp/wrap.go
generated
vendored
9
vendor/github.com/olekukonko/tablewriter/pkg/twwarp/wrap.go
generated
vendored
|
|
@ -12,8 +12,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
|
"github.com/clipperhouse/uax29/v2/graphemes"
|
||||||
"github.com/olekukonko/tablewriter/pkg/twwidth" // IMPORT YOUR NEW PACKAGE
|
"github.com/olekukonko/tablewriter/pkg/twwidth" // IMPORT YOUR NEW PACKAGE
|
||||||
"github.com/rivo/uniseg"
|
|
||||||
// "github.com/mattn/go-runewidth" // This can be removed if all direct uses are gone
|
// "github.com/mattn/go-runewidth" // This can be removed if all direct uses are gone
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -153,9 +153,9 @@ func stringToDisplayWidth(s string, targetWidth int) (substring string, actualWi
|
||||||
var currentWidth int
|
var currentWidth int
|
||||||
var endIndex int // Tracks the byte index in the original string
|
var endIndex int // Tracks the byte index in the original string
|
||||||
|
|
||||||
g := uniseg.NewGraphemes(s)
|
g := graphemes.FromString(s)
|
||||||
for g.Next() {
|
for g.Next() {
|
||||||
grapheme := g.Str()
|
grapheme := g.Value()
|
||||||
// graphemeWidth := runewidth.StringWidth(grapheme) // OLD
|
// graphemeWidth := runewidth.StringWidth(grapheme) // OLD
|
||||||
graphemeWidth := twwidth.Width(grapheme) // NEW: Use twdw.Width
|
graphemeWidth := twwidth.Width(grapheme) // NEW: Use twdw.Width
|
||||||
|
|
||||||
|
|
@ -164,8 +164,7 @@ func stringToDisplayWidth(s string, targetWidth int) (substring string, actualWi
|
||||||
}
|
}
|
||||||
|
|
||||||
currentWidth += graphemeWidth
|
currentWidth += graphemeWidth
|
||||||
_, e := g.Positions()
|
endIndex = g.End()
|
||||||
endIndex = e
|
|
||||||
}
|
}
|
||||||
return s[:endIndex], currentWidth
|
return s[:endIndex], currentWidth
|
||||||
}
|
}
|
||||||
|
|
|
||||||
252
vendor/github.com/olekukonko/tablewriter/pkg/twwidth/width.go
generated
vendored
252
vendor/github.com/olekukonko/tablewriter/pkg/twwidth/width.go
generated
vendored
|
|
@ -6,32 +6,55 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/clipperhouse/displaywidth"
|
||||||
"github.com/mattn/go-runewidth"
|
"github.com/mattn/go-runewidth"
|
||||||
|
"github.com/olekukonko/tablewriter/pkg/twcache"
|
||||||
)
|
)
|
||||||
|
|
||||||
// condition holds the global runewidth configuration, including East Asian width settings.
|
const (
|
||||||
var condition *runewidth.Condition
|
cacheCapacity = 8192
|
||||||
|
|
||||||
// mu protects access to condition and widthCache for thread safety.
|
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
|
||||||
|
|
||||||
|
// mu protects access to globalOptions for thread safety.
|
||||||
var mu sync.Mutex
|
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.
|
// ansi is a compiled regular expression for stripping ANSI escape codes from strings.
|
||||||
var ansi = Filter()
|
var ansi = Filter()
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
condition = runewidth.NewCondition()
|
// Initialize global options by detecting from the environment,
|
||||||
widthCache = make(map[cacheKey]int)
|
// 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
// cacheKey is used as a key for memoizing string width results in widthCache.
|
// makeCacheKey generates a string key for the LRU cache from the input string
|
||||||
type cacheKey struct {
|
// and the current East Asian width setting.
|
||||||
str string // Input string
|
// Prefix "0:" for false, "1:" for true.
|
||||||
eastAsianWidth bool // East Asian width setting
|
func makeCacheKey(str string, eastAsianWidth bool) string {
|
||||||
|
if eastAsianWidth {
|
||||||
|
return cacheEastAsianPrefix + str
|
||||||
|
}
|
||||||
|
return cachePrefix + str
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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,
|
// Filter compiles and returns a regular expression for matching ANSI escape sequences,
|
||||||
// including CSI (Control Sequence Introducer) and OSC (Operating System Command) sequences.
|
// including CSI (Control Sequence Introducer) and OSC (Operating System Command) sequences.
|
||||||
// The returned regex can be used to strip ANSI codes from strings.
|
// The returned regex can be used to strip ANSI codes from strings.
|
||||||
|
|
@ -50,99 +73,102 @@ func Filter() *regexp.Regexp {
|
||||||
return regexp.MustCompile("(" + regCSI + "|" + regOSC + ")")
|
return regexp.MustCompile("(" + regCSI + "|" + regOSC + ")")
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetEastAsian enables or disables East Asian width handling for width calculations.
|
// SetOptions sets the global options for width calculation.
|
||||||
// When the setting changes, the width cache is cleared to ensure accuracy.
|
// 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.
|
||||||
// This function is thread-safe.
|
// This function is thread-safe.
|
||||||
//
|
//
|
||||||
// Example:
|
// Example:
|
||||||
//
|
//
|
||||||
// twdw.SetEastAsian(true) // Enable East Asian width handling
|
// twdw.SetEastAsian(true) // Enable East Asian width handling
|
||||||
func SetEastAsian(enable bool) {
|
func SetEastAsian(enable bool) {
|
||||||
mu.Lock()
|
SetOptions(Options{EastAsianWidth: enable})
|
||||||
defer mu.Unlock()
|
|
||||||
if condition.EastAsianWidth != enable {
|
|
||||||
condition.EastAsianWidth = enable
|
|
||||||
widthCache = make(map[cacheKey]int) // Clear cache on setting change
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetCondition updates the global runewidth.Condition used for width calculations.
|
// IsEastAsian returns the current East Asian width setting.
|
||||||
// When the condition is changed, the width cache is cleared.
|
|
||||||
// This function is thread-safe.
|
// This function is thread-safe.
|
||||||
//
|
//
|
||||||
// Example:
|
// Example:
|
||||||
//
|
//
|
||||||
// newCond := runewidth.NewCondition()
|
// if twdw.IsEastAsian() {
|
||||||
// newCond.EastAsianWidth = true
|
// // Handle East Asian width characters
|
||||||
// twdw.SetCondition(newCond)
|
// }
|
||||||
func SetCondition(newCond *runewidth.Condition) {
|
func IsEastAsian() bool {
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
defer mu.Unlock()
|
defer mu.Unlock()
|
||||||
condition = newCond
|
return globalOptions.EastAsianWidth
|
||||||
widthCache = make(map[cacheKey]int) // Clear cache on setting change
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Width calculates the visual width of a string, excluding ANSI escape sequences,
|
// Deprecated: use SetOptions with the new twwidth.Options struct instead.
|
||||||
// using the go-runewidth package for accurate Unicode handling. It accounts for the
|
// This function is kept for backward compatibility.
|
||||||
// current East Asian width setting and caches results for performance.
|
func SetCondition(cond *runewidth.Condition) {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
newEastAsianWidth := cond.EastAsianWidth
|
||||||
|
if globalOptions.EastAsianWidth != newEastAsianWidth {
|
||||||
|
globalOptions.EastAsianWidth = newEastAsianWidth
|
||||||
|
widthCache.Purge()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
// This function is thread-safe.
|
// This function is thread-safe.
|
||||||
//
|
//
|
||||||
// Example:
|
// Example:
|
||||||
//
|
//
|
||||||
// width := twdw.Width("Hello\x1b[31mWorld") // Returns 10
|
// width := twdw.Width("Hello\x1b[31mWorld") // Returns 10
|
||||||
func Width(str string) int {
|
func Width(str string) int {
|
||||||
mu.Lock()
|
currentEA := IsEastAsian()
|
||||||
key := cacheKey{str: str, eastAsianWidth: condition.EastAsianWidth}
|
key := makeCacheKey(str, currentEA)
|
||||||
if w, found := widthCache[key]; found {
|
|
||||||
mu.Unlock()
|
if w, found := widthCache.Get(key); found {
|
||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
mu.Unlock()
|
|
||||||
|
|
||||||
// Use a temporary condition to avoid holding the lock during calculation
|
|
||||||
tempCond := runewidth.NewCondition()
|
|
||||||
tempCond.EastAsianWidth = key.eastAsianWidth
|
|
||||||
|
|
||||||
|
opts := displaywidth.Options{EastAsianWidth: currentEA}
|
||||||
stripped := ansi.ReplaceAllLiteralString(str, "")
|
stripped := ansi.ReplaceAllLiteralString(str, "")
|
||||||
calculatedWidth := tempCond.StringWidth(stripped)
|
calculatedWidth := opts.String(stripped)
|
||||||
|
|
||||||
mu.Lock()
|
|
||||||
widthCache[key] = calculatedWidth
|
|
||||||
mu.Unlock()
|
|
||||||
|
|
||||||
|
widthCache.Add(key, calculatedWidth)
|
||||||
return calculatedWidth
|
return calculatedWidth
|
||||||
}
|
}
|
||||||
|
|
||||||
// WidthNoCache calculates the visual width of a string without using or
|
// WidthWithOptions calculates the visual width of a string with specific options,
|
||||||
// updating the global cache. It uses the current global East Asian width setting.
|
// bypassing the global settings and cache. This is useful for one-shot calculations
|
||||||
// This function is intended for internal use (e.g., benchmarking) and is thread-safe.
|
// 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.
|
||||||
//
|
//
|
||||||
// Example:
|
// Example:
|
||||||
//
|
//
|
||||||
// width := twdw.WidthNoCache("Hello\x1b[31mWorld") // Returns 10
|
// width := twdw.WidthNoCache("Hello\x1b[31mWorld") // Returns 10
|
||||||
func WidthNoCache(str string) int {
|
func WidthNoCache(str string) int {
|
||||||
mu.Lock()
|
// This function's behavior is equivalent to a one-shot calculation
|
||||||
currentEA := condition.EastAsianWidth
|
// using the current global options. The WidthWithOptions function
|
||||||
mu.Unlock()
|
// does not interact with the cache, thus fulfilling the requirement.
|
||||||
|
return WidthWithOptions(str, Options{EastAsianWidth: IsEastAsian()})
|
||||||
tempCond := runewidth.NewCondition()
|
|
||||||
tempCond.EastAsianWidth = currentEA
|
|
||||||
|
|
||||||
stripped := ansi.ReplaceAllLiteralString(str, "")
|
|
||||||
return tempCond.StringWidth(stripped)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display calculates the visual width of a string, excluding ANSI escape sequences,
|
// Deprecated: use WidthWithOptions with the new twwidth.Options struct instead.
|
||||||
// using the provided runewidth condition. Unlike Width, it does not use caching
|
// This function is kept for backward compatibility.
|
||||||
// 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 {
|
func Display(cond *runewidth.Condition, str string) int {
|
||||||
return cond.StringWidth(ansi.ReplaceAllLiteralString(str, ""))
|
opts := Options{EastAsianWidth: cond.EastAsianWidth}
|
||||||
|
return WidthWithOptions(str, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Truncate shortens a string to fit within a specified visual width, optionally
|
// Truncate shortens a string to fit within a specified visual width, optionally
|
||||||
|
|
@ -189,31 +215,34 @@ func Truncate(s string, maxWidth int, suffix ...string) string {
|
||||||
// Case 3: String fits completely or fits with suffix.
|
// Case 3: String fits completely or fits with suffix.
|
||||||
// Here, maxWidth is the total budget for the line.
|
// Here, maxWidth is the total budget for the line.
|
||||||
if sDisplayWidth <= maxWidth {
|
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.
|
if len(suffixStr) == 0 { // No suffix.
|
||||||
return s
|
return safeS
|
||||||
}
|
}
|
||||||
// Suffix is provided. Check if s + suffix fits.
|
// Suffix is provided. Check if s + suffix fits.
|
||||||
if sDisplayWidth+suffixDisplayWidth <= maxWidth {
|
if sDisplayWidth+suffixDisplayWidth <= maxWidth {
|
||||||
return s + suffixStr
|
return safeS + suffixStr
|
||||||
}
|
}
|
||||||
// s fits, but s + suffix is too long. Return s.
|
// s fits, but s + suffix is too long. Return s (with reset if needed).
|
||||||
return s
|
return safeS
|
||||||
}
|
}
|
||||||
|
|
||||||
// Case 4: String needs truncation (sDisplayWidth > maxWidth).
|
// Case 4: String needs truncation (sDisplayWidth > maxWidth).
|
||||||
// maxWidth is the total budget for the final string (content + suffix).
|
// 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 := condition.EastAsianWidth
|
|
||||||
mu.Unlock()
|
|
||||||
|
|
||||||
// Special case for EastAsian true: if only suffix fits, return suffix.
|
// Special case for EastAsian true: if only suffix fits, return suffix.
|
||||||
// This was derived from previous test behavior.
|
// This was derived from previous test behavior.
|
||||||
if len(suffixStr) > 0 && currentGlobalEastAsianWidth {
|
if len(suffixStr) > 0 && currentGlobalEastAsianWidth {
|
||||||
provisionalContentWidth := maxWidth - suffixDisplayWidth
|
provisionalContentWidth := maxWidth - suffixDisplayWidth
|
||||||
if provisionalContentWidth == 0 { // Exactly enough space for suffix only
|
if provisionalContentWidth == 0 { // Exactly enough space for suffix only
|
||||||
return suffixStr // <<<< MODIFIED: No ANSI reset here
|
return suffixStr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -235,7 +264,6 @@ func Truncate(s string, maxWidth int, suffix ...string) string {
|
||||||
}
|
}
|
||||||
return "" // Cannot fit anything.
|
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 contentBuf bytes.Buffer
|
||||||
var currentContentDisplayWidth int
|
var currentContentDisplayWidth int
|
||||||
|
|
@ -243,8 +271,7 @@ func Truncate(s string, maxWidth int, suffix ...string) string {
|
||||||
inAnsiSequence := false
|
inAnsiSequence := false
|
||||||
ansiWrittenToContent := false
|
ansiWrittenToContent := false
|
||||||
|
|
||||||
localRunewidthCond := runewidth.NewCondition()
|
dwOpts := displaywidth.Options{EastAsianWidth: currentGlobalEastAsianWidth}
|
||||||
localRunewidthCond.EastAsianWidth = currentGlobalEastAsianWidth
|
|
||||||
|
|
||||||
for _, r := range s {
|
for _, r := range s {
|
||||||
if r == '\x1b' {
|
if r == '\x1b' {
|
||||||
|
|
@ -278,7 +305,7 @@ func Truncate(s string, maxWidth int, suffix ...string) string {
|
||||||
ansiSeqBuf.Reset()
|
ansiSeqBuf.Reset()
|
||||||
}
|
}
|
||||||
} else { // Normal character
|
} else { // Normal character
|
||||||
runeDisplayWidth := localRunewidthCond.RuneWidth(r)
|
runeDisplayWidth := dwOpts.Rune(r)
|
||||||
if targetContentForIteration == 0 { // No budget for content at all
|
if targetContentForIteration == 0 { // No budget for content at all
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
@ -292,32 +319,51 @@ func Truncate(s string, maxWidth int, suffix ...string) string {
|
||||||
|
|
||||||
result := contentBuf.String()
|
result := contentBuf.String()
|
||||||
|
|
||||||
// Suffix is added if:
|
// Determine if we need to append a reset sequence to prevent color bleeding.
|
||||||
// 1. A suffix string is provided.
|
// This is needed if we wrote any ANSI codes or if the input had active codes
|
||||||
// 2. Truncation actually happened (sDisplayWidth > maxWidth originally)
|
// that we might have cut off or left open.
|
||||||
// OR if the content part is empty but a suffix is meant to be shown
|
needsReset := false
|
||||||
// (e.g. targetContentForIteration was 0).
|
if (ansiWrittenToContent || (inAnsiSequence && strings.Contains(s, "\x1b["))) && (currentContentDisplayWidth > 0 || ansiWrittenToContent) {
|
||||||
if len(suffixStr) > 0 {
|
if !strings.HasSuffix(result, "\x1b[0m") {
|
||||||
// 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
|
needsReset = true
|
||||||
}
|
}
|
||||||
|
} else if currentContentDisplayWidth > 0 && strings.Contains(result, "\x1b[") && !strings.HasSuffix(result, "\x1b[0m") && strings.Contains(s, "\x1b[") {
|
||||||
|
needsReset = true
|
||||||
|
}
|
||||||
|
|
||||||
if needsReset {
|
if needsReset {
|
||||||
result += "\x1b[0m"
|
result += "\x1b[0m"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Suffix is added if provided.
|
||||||
|
if len(suffixStr) > 0 {
|
||||||
result += suffixStr
|
result += suffixStr
|
||||||
}
|
}
|
||||||
return result
|
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()
|
||||||
|
}
|
||||||
|
|
|
||||||
76
vendor/github.com/olekukonko/tablewriter/tablewriter.go
generated
vendored
76
vendor/github.com/olekukonko/tablewriter/tablewriter.go
generated
vendored
|
|
@ -8,11 +8,11 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/olekukonko/errors"
|
"github.com/olekukonko/errors"
|
||||||
"github.com/olekukonko/ll"
|
"github.com/olekukonko/ll"
|
||||||
"github.com/olekukonko/ll/lh"
|
"github.com/olekukonko/ll/lh"
|
||||||
|
"github.com/olekukonko/tablewriter/pkg/twcache"
|
||||||
"github.com/olekukonko/tablewriter/pkg/twwarp"
|
"github.com/olekukonko/tablewriter/pkg/twwarp"
|
||||||
"github.com/olekukonko/tablewriter/pkg/twwidth"
|
"github.com/olekukonko/tablewriter/pkg/twwidth"
|
||||||
"github.com/olekukonko/tablewriter/renderer"
|
"github.com/olekukonko/tablewriter/renderer"
|
||||||
|
|
@ -23,7 +23,7 @@ import (
|
||||||
type Table struct {
|
type Table struct {
|
||||||
writer io.Writer // Destination for table output
|
writer io.Writer // Destination for table output
|
||||||
counters []tw.Counter // Counters for indices
|
counters []tw.Counter // Counters for indices
|
||||||
rows [][][]string // Row data, supporting multi-line cells
|
rows [][]string // Row data, one slice of strings per logical row
|
||||||
headers [][]string // Header content
|
headers [][]string // Header content
|
||||||
footers [][]string // Footer content
|
footers [][]string // Footer content
|
||||||
headerWidths tw.Mapper[int, int] // Computed widths for header columns
|
headerWidths tw.Mapper[int, int] // Computed widths for header columns
|
||||||
|
|
@ -52,9 +52,7 @@ type Table struct {
|
||||||
streamRowCounter int // Counter for rows rendered in streaming mode (0-indexed logical rows)
|
streamRowCounter int // Counter for rows rendered in streaming mode (0-indexed logical rows)
|
||||||
|
|
||||||
// cache
|
// cache
|
||||||
stringerCache map[reflect.Type]reflect.Value // Cache for stringer reflection
|
stringerCache twcache.Cache[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
|
batchRenderNumCols int
|
||||||
isBatchRenderNumColsSet bool
|
isBatchRenderNumColsSet bool
|
||||||
|
|
@ -126,8 +124,7 @@ func NewTable(w io.Writer, opts ...Option) *Table {
|
||||||
streamRowCounter: 0,
|
streamRowCounter: 0,
|
||||||
|
|
||||||
// Cache
|
// Cache
|
||||||
stringerCache: make(map[reflect.Type]reflect.Value),
|
stringerCache: twcache.NewLRU[reflect.Type, reflect.Value](tw.DefaultCacheStringCapacity),
|
||||||
stringerCacheEnabled: false, // Disabled by default
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// set Options
|
// set Options
|
||||||
|
|
@ -221,13 +218,12 @@ func (t *Table) Append(rows ...interface{}) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The rest of the function proceeds as before, converting the data to string lines.
|
cells, err := t.convertCellsToStrings(cellsSource, t.config.Row)
|
||||||
lines, err := t.toStringLines(cellsSource, t.config.Row)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.logger.Errorf("Append (Batch) failed for cellsSource %v: %v", cellsSource, err)
|
t.logger.Errorf("Append (Batch) failed for cellsSource %v: %v", cellsSource, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
t.rows = append(t.rows, lines)
|
t.rows = append(t.rows, cells)
|
||||||
|
|
||||||
t.logger.Debugf("Append (Batch) completed for one row, total rows in table: %d", len(t.rows))
|
t.logger.Debugf("Append (Batch) completed for one row, total rows in table: %d", len(t.rows))
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -456,7 +452,7 @@ func (t *Table) Reset() {
|
||||||
t.logger.Debug("Reset() called. Clearing table data and render state.")
|
t.logger.Debug("Reset() called. Clearing table data and render state.")
|
||||||
|
|
||||||
// Clear data slices
|
// Clear data slices
|
||||||
t.rows = nil // Or t.rows = make([][][]string, 0)
|
t.rows = nil // Or t.rows = make([][]string, 0)
|
||||||
t.headers = nil // Or t.headers = make([][]string, 0)
|
t.headers = nil // Or t.headers = make([][]string, 0)
|
||||||
t.footers = nil // Or t.footers = make([][]string, 0)
|
t.footers = nil // Or t.footers = make([][]string, 0)
|
||||||
|
|
||||||
|
|
@ -484,10 +480,11 @@ func (t *Table) Reset() {
|
||||||
t.streamRowCounter = 0
|
t.streamRowCounter = 0
|
||||||
|
|
||||||
// The stringer and its cache are part of the table's configuration,
|
// The stringer and its cache are part of the table's configuration,
|
||||||
if t.stringerCacheEnabled {
|
if t.stringerCache == nil {
|
||||||
t.stringerCacheMu.Lock()
|
t.stringerCache = twcache.NewLRU[reflect.Type, reflect.Value](tw.DefaultCacheStringCapacity)
|
||||||
t.stringerCache = make(map[reflect.Type]reflect.Value)
|
t.logger.Debug("Reset(): Stringer cache reset to default capacity.")
|
||||||
t.stringerCacheMu.Unlock()
|
} else {
|
||||||
|
t.stringerCache.Purge()
|
||||||
t.logger.Debug("Reset(): Stringer cache cleared.")
|
t.logger.Debug("Reset(): Stringer cache cleared.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -556,16 +553,14 @@ func (t *Table) appendSingle(row interface{}) error {
|
||||||
t.logger.Debugf("appendSingle: Dispatching to streamAppendRow for row: %v", row)
|
t.logger.Debugf("appendSingle: Dispatching to streamAppendRow for row: %v", row)
|
||||||
return t.streamAppendRow(row) // Call the streaming render function
|
return t.streamAppendRow(row) // Call the streaming render function
|
||||||
}
|
}
|
||||||
// Existing batch logic:
|
|
||||||
t.logger.Debugf("appendSingle: Processing for batch mode, row: %v", row)
|
t.logger.Debugf("appendSingle: Processing for batch mode, row: %v", row)
|
||||||
// toStringLines now uses the new convertCellsToStrings internally, then prepareContent.
|
cells, err := t.convertCellsToStrings(row, t.config.Row)
|
||||||
// This is fine for batch.
|
|
||||||
lines, err := t.toStringLines(row, t.config.Row)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.logger.Debugf("Error in toStringLines (batch mode): %v", err)
|
t.logger.Debugf("Error in convertCellsToStrings (batch mode): %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
t.rows = append(t.rows, lines) // Add to batch storage
|
t.rows = append(t.rows, cells) // Add to batch storage
|
||||||
t.logger.Debugf("Row appended to batch t.rows, total batch rows: %d", len(t.rows))
|
t.logger.Debugf("Row appended to batch t.rows, total batch rows: %d", len(t.rows))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -775,8 +770,8 @@ func (t *Table) maxColumns() int {
|
||||||
m = len(t.headers[0])
|
m = len(t.headers[0])
|
||||||
}
|
}
|
||||||
for _, row := range t.rows {
|
for _, row := range t.rows {
|
||||||
if len(row) > 0 && len(row[0]) > m {
|
if len(row) > m {
|
||||||
m = len(row[0])
|
m = len(row)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(t.footers) > 0 && len(t.footers[0]) > m {
|
if len(t.footers) > 0 && len(t.footers[0]) > m {
|
||||||
|
|
@ -811,7 +806,7 @@ func (t *Table) printTopBottomCaption(w io.Writer, actualTableWidth int) {
|
||||||
t.logger.Debugf("[printCaption] Empty table, no user caption.Width: Using natural caption width %d.", captionWrapWidth)
|
t.logger.Debugf("[printCaption] Empty table, no user caption.Width: Using natural caption width %d.", captionWrapWidth)
|
||||||
} else {
|
} else {
|
||||||
captionWrapWidth = actualTableWidth
|
captionWrapWidth = actualTableWidth
|
||||||
t.logger.Debugf("[printCaption] Non-empty table, no user caption.Width: Using actualTableWidth %d for wrapping.", actualTableWidth)
|
t.logger.Debugf("[printCaption] Non-empty table, no user caption.Width: Using actualTableWidth %d for wrapping.", captionWrapWidth)
|
||||||
}
|
}
|
||||||
|
|
||||||
if captionWrapWidth <= 0 {
|
if captionWrapWidth <= 0 {
|
||||||
|
|
@ -1066,13 +1061,20 @@ func (t *Table) prepareContexts() (*renderContext, *mergeContext, error) {
|
||||||
logger: t.logger,
|
logger: t.logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
isEmpty, visibleCount := t.getEmptyColumnInfo(numOriginalCols)
|
// Process raw rows into visual, multi-line rows
|
||||||
|
processedRowLines := make([][][]string, len(t.rows))
|
||||||
|
for i, rawRow := range t.rows {
|
||||||
|
processedRowLines[i] = t.prepareContent(rawRow, t.config.Row)
|
||||||
|
}
|
||||||
|
ctx.rowLines = processedRowLines
|
||||||
|
|
||||||
|
isEmpty, visibleCount := t.getEmptyColumnInfo(ctx.rowLines, numOriginalCols)
|
||||||
ctx.emptyColumns = isEmpty
|
ctx.emptyColumns = isEmpty
|
||||||
ctx.visibleColCount = visibleCount
|
ctx.visibleColCount = visibleCount
|
||||||
|
|
||||||
mctx := &mergeContext{
|
mctx := &mergeContext{
|
||||||
headerMerges: make(map[int]tw.MergeState),
|
headerMerges: make(map[int]tw.MergeState),
|
||||||
rowMerges: make([]map[int]tw.MergeState, len(t.rows)),
|
rowMerges: make([]map[int]tw.MergeState, len(ctx.rowLines)),
|
||||||
footerMerges: make(map[int]tw.MergeState),
|
footerMerges: make(map[int]tw.MergeState),
|
||||||
horzMerges: make(map[tw.Position]map[int]bool),
|
horzMerges: make(map[tw.Position]map[int]bool),
|
||||||
}
|
}
|
||||||
|
|
@ -1081,7 +1083,6 @@ func (t *Table) prepareContexts() (*renderContext, *mergeContext, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.headerLines = t.headers
|
ctx.headerLines = t.headers
|
||||||
ctx.rowLines = t.rows
|
|
||||||
ctx.footerLines = t.footers
|
ctx.footerLines = t.footers
|
||||||
|
|
||||||
if err := t.calculateAndNormalizeWidths(ctx); err != nil {
|
if err := t.calculateAndNormalizeWidths(ctx); err != nil {
|
||||||
|
|
@ -1095,21 +1096,28 @@ func (t *Table) prepareContexts() (*renderContext, *mergeContext, error) {
|
||||||
ctx.headerLines = preparedHeaderLines
|
ctx.headerLines = preparedHeaderLines
|
||||||
mctx.headerMerges = headerMerges
|
mctx.headerMerges = headerMerges
|
||||||
|
|
||||||
processedRowLines := make([][][]string, len(ctx.rowLines))
|
// Re-process row lines for merges now that widths are known
|
||||||
|
processedRowLinesWithMerges := make([][][]string, len(ctx.rowLines))
|
||||||
for i, row := range ctx.rowLines {
|
for i, row := range ctx.rowLines {
|
||||||
if mctx.rowMerges[i] == nil {
|
if mctx.rowMerges[i] == nil {
|
||||||
mctx.rowMerges[i] = make(map[int]tw.MergeState)
|
mctx.rowMerges[i] = make(map[int]tw.MergeState)
|
||||||
}
|
}
|
||||||
processedRowLines[i], mctx.rowMerges[i], _ = t.prepareWithMerges(row, t.config.Row, tw.Row)
|
processedRowLinesWithMerges[i], mctx.rowMerges[i], _ = t.prepareWithMerges(row, t.config.Row, tw.Row)
|
||||||
}
|
}
|
||||||
ctx.rowLines = processedRowLines
|
ctx.rowLines = processedRowLinesWithMerges
|
||||||
|
|
||||||
t.applyHorizontalMergeWidths(tw.Header, ctx, mctx.headerMerges)
|
t.applyHorizontalMerges(tw.Header, ctx, mctx.headerMerges)
|
||||||
|
|
||||||
if t.config.Row.Formatting.MergeMode&tw.MergeVertical != 0 {
|
mergeMode := t.config.Row.Merging.Mode
|
||||||
|
if mergeMode == 0 {
|
||||||
|
mergeMode = t.config.Row.Formatting.MergeMode
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now check against the effective mode
|
||||||
|
if mergeMode&tw.MergeVertical != 0 {
|
||||||
t.applyVerticalMerges(ctx, mctx)
|
t.applyVerticalMerges(ctx, mctx)
|
||||||
}
|
}
|
||||||
if t.config.Row.Formatting.MergeMode&tw.MergeHierarchical != 0 {
|
if mergeMode&tw.MergeHierarchical != 0 {
|
||||||
t.applyHierarchicalMerges(ctx, mctx)
|
t.applyHierarchicalMerges(ctx, mctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1166,7 +1174,7 @@ func (t *Table) prepareFooter(ctx *renderContext, mctx *mergeContext) {
|
||||||
mctx.footerMerges = mergeStates
|
mctx.footerMerges = mergeStates
|
||||||
ctx.footerLines = t.footers
|
ctx.footerLines = t.footers
|
||||||
t.logger.Debugf("Base footer widths (normalized from rows/header): %v", ctx.widths[tw.Footer])
|
t.logger.Debugf("Base footer widths (normalized from rows/header): %v", ctx.widths[tw.Footer])
|
||||||
t.applyHorizontalMergeWidths(tw.Footer, ctx, mctx.footerMerges)
|
t.applyHorizontalMerges(tw.Footer, ctx, mctx.footerMerges)
|
||||||
ctx.footerPrepared = true
|
ctx.footerPrepared = true
|
||||||
t.logger.Debugf("Footer preparation completed. Final footer widths: %v", ctx.widths[tw.Footer])
|
t.logger.Debugf("Footer preparation completed. Final footer widths: %v", ctx.widths[tw.Footer])
|
||||||
}
|
}
|
||||||
|
|
|
||||||
32
vendor/github.com/olekukonko/tablewriter/tw/cell.go
generated
vendored
32
vendor/github.com/olekukonko/tablewriter/tw/cell.go
generated
vendored
|
|
@ -2,16 +2,31 @@ package tw
|
||||||
|
|
||||||
// CellFormatting holds formatting options for table cells.
|
// CellFormatting holds formatting options for table cells.
|
||||||
type CellFormatting struct {
|
type CellFormatting struct {
|
||||||
AutoWrap int // Wrapping behavior (e.g., WrapTruncate, WrapNormal)
|
AutoWrap int // Wrapping behavior (e.g., WrapTruncate, WrapNormal)
|
||||||
MergeMode int // Bitmask for merge behavior (e.g., MergeHorizontal, MergeVertical)
|
|
||||||
|
|
||||||
// Changed form bool to State
|
|
||||||
// See https://github.com/olekukonko/tablewriter/issues/261
|
|
||||||
AutoFormat State // Enables automatic formatting (e.g., title case for headers)
|
AutoFormat State // Enables automatic formatting (e.g., title case for headers)
|
||||||
|
|
||||||
// Deprecated: kept for compatibility
|
// Deprecated: Kept for backward compatibility. Use CellConfig.CellMerging.Mode instead.
|
||||||
// will be removed soon
|
// This will be removed in a future version.
|
||||||
Alignment Align // Text alignment within the cell (e.g., Left, Right, Center)
|
MergeMode int
|
||||||
|
|
||||||
|
// Deprecated: Kept for backward compatibility. Use CellConfig.Alignment instead.
|
||||||
|
// This will be removed in a future version.
|
||||||
|
Alignment Align
|
||||||
|
}
|
||||||
|
|
||||||
|
// CellMerging holds the configuration for how cells should be merged.
|
||||||
|
// This new struct replaces the deprecated MergeMode.
|
||||||
|
type CellMerging struct {
|
||||||
|
// Mode is a bitmask specifying the type of merge (e.g., MergeHorizontal, MergeVertical).
|
||||||
|
Mode int
|
||||||
|
|
||||||
|
// ByColumnIndex specifies which column indices should be considered for merging.
|
||||||
|
// If the mapper is nil or empty, merging applies to all columns (if Mode is set).
|
||||||
|
// Otherwise, only columns with an index present as a key will be merged.
|
||||||
|
ByColumnIndex Mapper[int, bool]
|
||||||
|
|
||||||
|
// ByRowIndex is reserved for future features to specify merging on specific rows.
|
||||||
|
ByRowIndex Mapper[int, bool]
|
||||||
}
|
}
|
||||||
|
|
||||||
// CellPadding defines padding settings for table cells.
|
// CellPadding defines padding settings for table cells.
|
||||||
|
|
@ -47,6 +62,7 @@ type CellConfig struct {
|
||||||
Filter CellFilter // Function to filter cell content (renamed from Filter Filter)
|
Filter CellFilter // Function to filter cell content (renamed from Filter Filter)
|
||||||
Alignment CellAlignment // Alignment configuration for cells
|
Alignment CellAlignment // Alignment configuration for cells
|
||||||
ColMaxWidths CellWidth // Per-column maximum width overrides
|
ColMaxWidths CellWidth // Per-column maximum width overrides
|
||||||
|
Merging CellMerging // Merging holds all configuration related to cell merging.
|
||||||
|
|
||||||
// Deprecated: use Alignment.PerColumn instead. Will be removed in a future version.
|
// Deprecated: use Alignment.PerColumn instead. Will be removed in a future version.
|
||||||
// will be removed soon
|
// will be removed soon
|
||||||
|
|
|
||||||
33
vendor/github.com/olekukonko/tablewriter/tw/mapper.go
generated
vendored
33
vendor/github.com/olekukonko/tablewriter/tw/mapper.go
generated
vendored
|
|
@ -210,3 +210,36 @@ func (m Mapper[K, V]) SortedKeys() []K {
|
||||||
|
|
||||||
return keys
|
return keys
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewBoolMapper[K comparable](keys ...K) Mapper[K, bool] {
|
||||||
|
if len(keys) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
mapper := NewMapper[K, bool]()
|
||||||
|
for _, key := range keys {
|
||||||
|
mapper.Set(key, true)
|
||||||
|
}
|
||||||
|
return mapper
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIntMapper[K comparable](keys ...K) Mapper[K, int] {
|
||||||
|
if len(keys) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
mapper := NewMapper[K, int]()
|
||||||
|
for _, key := range keys {
|
||||||
|
mapper.Set(key, 0)
|
||||||
|
}
|
||||||
|
return mapper
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIdentityMapper[K comparable](keys ...K) Mapper[K, K] {
|
||||||
|
if len(keys) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
mapper := NewMapper[K, K]()
|
||||||
|
for _, key := range keys {
|
||||||
|
mapper.Set(key, key)
|
||||||
|
}
|
||||||
|
return mapper
|
||||||
|
}
|
||||||
|
|
|
||||||
2
vendor/github.com/olekukonko/tablewriter/tw/tw.go
generated
vendored
2
vendor/github.com/olekukonko/tablewriter/tw/tw.go
generated
vendored
|
|
@ -8,6 +8,8 @@ const (
|
||||||
Success = 1 // Operation succeeded
|
Success = 1 // Operation succeeded
|
||||||
|
|
||||||
MinimumColumnWidth = 8
|
MinimumColumnWidth = 8
|
||||||
|
|
||||||
|
DefaultCacheStringCapacity = 10 * 1024 // 10 KB
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
||||||
112
vendor/github.com/olekukonko/tablewriter/zoo.go
generated
vendored
112
vendor/github.com/olekukonko/tablewriter/zoo.go
generated
vendored
|
|
@ -18,7 +18,23 @@ import (
|
||||||
// Parameters ctx and mctx hold rendering and merge state.
|
// Parameters ctx and mctx hold rendering and merge state.
|
||||||
// No return value.
|
// No return value.
|
||||||
func (t *Table) applyHierarchicalMerges(ctx *renderContext, mctx *mergeContext) {
|
func (t *Table) applyHierarchicalMerges(ctx *renderContext, mctx *mergeContext) {
|
||||||
ctx.logger.Debug("Applying hierarchical merges (left-to-right vertical flow - snapshot comparison)")
|
// First, ensure we should even run this logic.
|
||||||
|
// Check both the new CellMerging struct and the deprecated Formatting field.
|
||||||
|
mergeMode := t.config.Row.Merging.Mode
|
||||||
|
if mergeMode == 0 {
|
||||||
|
mergeMode = t.config.Row.Formatting.MergeMode
|
||||||
|
}
|
||||||
|
if !(mergeMode&tw.MergeHierarchical != 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mergeColumnMapper := t.config.Row.Merging.ByColumnIndex
|
||||||
|
if mergeColumnMapper != nil {
|
||||||
|
ctx.logger.Debugf("Applying hierarchical merges ONLY to specified columns: %v", mergeColumnMapper.Keys())
|
||||||
|
} else {
|
||||||
|
ctx.logger.Debug("Applying hierarchical merges (left-to-right vertical flow - snapshot comparison)")
|
||||||
|
}
|
||||||
|
|
||||||
if len(ctx.rowLines) <= 1 {
|
if len(ctx.rowLines) <= 1 {
|
||||||
ctx.logger.Debug("Skipping hierarchical merges - less than 2 rows")
|
ctx.logger.Debug("Skipping hierarchical merges - less than 2 rows")
|
||||||
return
|
return
|
||||||
|
|
@ -41,6 +57,12 @@ func (t *Table) applyHierarchicalMerges(ctx *renderContext, mctx *mergeContext)
|
||||||
leftCellContinuedHierarchical := false
|
leftCellContinuedHierarchical := false
|
||||||
|
|
||||||
for c := 0; c < numCols; c++ {
|
for c := 0; c < numCols; c++ {
|
||||||
|
// If a column map is specified, skip columns that are not in it.
|
||||||
|
if mergeColumnMapper != nil && !mergeColumnMapper.Has(c) {
|
||||||
|
leftCellContinuedHierarchical = false // Reset hierarchy tracking
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if mctx.rowMerges[r] == nil {
|
if mctx.rowMerges[r] == nil {
|
||||||
mctx.rowMerges[r] = make(map[int]tw.MergeState)
|
mctx.rowMerges[r] = make(map[int]tw.MergeState)
|
||||||
}
|
}
|
||||||
|
|
@ -146,15 +168,15 @@ func (t *Table) applyHierarchicalMerges(ctx *renderContext, mctx *mergeContext)
|
||||||
ctx.logger.Debug("Hierarchical merge processing completed")
|
ctx.logger.Debug("Hierarchical merge processing completed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// applyHorizontalMergeWidths adjusts column widths for horizontal merges.
|
// applyHorizontalMerges adjusts column widths for horizontal merges.
|
||||||
// Parameters include position, ctx for rendering, and mergeStates for merges.
|
// Parameters include position, ctx for rendering, and mergeStates for merges.
|
||||||
// No return value.
|
// No return value.
|
||||||
func (t *Table) applyHorizontalMergeWidths(position tw.Position, ctx *renderContext, mergeStates map[int]tw.MergeState) {
|
func (t *Table) applyHorizontalMerges(position tw.Position, ctx *renderContext, mergeStates map[int]tw.MergeState) {
|
||||||
if mergeStates == nil {
|
if mergeStates == nil {
|
||||||
t.logger.Debugf("applyHorizontalMergeWidths: Skipping %s - no merge states", position)
|
t.logger.Debugf("applyHorizontalMerges: Skipping %s - no merge states", position)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t.logger.Debugf("applyHorizontalMergeWidths: Applying HMerge width recalc for %s", position)
|
t.logger.Debugf("applyHorizontalMerges: Applying HMerge width recalc for %s", position)
|
||||||
|
|
||||||
numCols := ctx.numCols
|
numCols := ctx.numCols
|
||||||
targetWidthsMap := ctx.widths[position]
|
targetWidthsMap := ctx.widths[position]
|
||||||
|
|
@ -211,16 +233,31 @@ func (t *Table) applyHorizontalMergeWidths(position tw.Position, ctx *renderCont
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.logger.Debugf("applyHorizontalMergeWidths: Final widths for %s: %v", position, targetWidthsMap)
|
ctx.logger.Debugf("applyHorizontalMerges: Final widths for %s: %v", position, targetWidthsMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
// applyVerticalMerges applies vertical merges to row content.
|
// applyVerticalMerges applies vertical merges to row content.
|
||||||
// Parameters ctx and mctx hold rendering and merge state.
|
// Parameters ctx and mctx hold rendering and merge state.
|
||||||
// No return value.
|
// No return value.
|
||||||
func (t *Table) applyVerticalMerges(ctx *renderContext, mctx *mergeContext) {
|
func (t *Table) applyVerticalMerges(ctx *renderContext, mctx *mergeContext) {
|
||||||
ctx.logger.Debugf("Applying vertical merges across %d rows", len(ctx.rowLines))
|
// First, ensure we should even run this logic.
|
||||||
numCols := ctx.numCols
|
// Check both the new CellMerging struct and the deprecated Formatting field.
|
||||||
|
mergeMode := t.config.Row.Merging.Mode
|
||||||
|
if mergeMode == 0 {
|
||||||
|
mergeMode = t.config.Row.Formatting.MergeMode
|
||||||
|
}
|
||||||
|
if !(mergeMode&tw.MergeVertical != 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mergeColumnMapper := t.config.Row.Merging.ByColumnIndex
|
||||||
|
if mergeColumnMapper != nil {
|
||||||
|
ctx.logger.Debugf("Applying vertical merges ONLY to specified columns: %v", mergeColumnMapper.Keys())
|
||||||
|
} else {
|
||||||
|
ctx.logger.Debugf("Applying vertical merges across %d rows", len(ctx.rowLines))
|
||||||
|
}
|
||||||
|
|
||||||
|
numCols := ctx.numCols
|
||||||
mergeStartRow := make(map[int]int)
|
mergeStartRow := make(map[int]int)
|
||||||
mergeStartContent := make(map[int]string)
|
mergeStartContent := make(map[int]string)
|
||||||
|
|
||||||
|
|
@ -243,6 +280,11 @@ func (t *Table) applyVerticalMerges(ctx *renderContext, mctx *mergeContext) {
|
||||||
currentLineContent := ctx.rowLines[i]
|
currentLineContent := ctx.rowLines[i]
|
||||||
|
|
||||||
for col := 0; col < numCols; col++ {
|
for col := 0; col < numCols; col++ {
|
||||||
|
// If a column map is specified, skip columns that are not in it.
|
||||||
|
if mergeColumnMapper != nil && !mergeColumnMapper.Has(col) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Join all lines of the cell to compare full content
|
// Join all lines of the cell to compare full content
|
||||||
var currentVal strings.Builder
|
var currentVal strings.Builder
|
||||||
for _, line := range currentLineContent {
|
for _, line := range currentLineContent {
|
||||||
|
|
@ -579,11 +621,6 @@ func (t *Table) calculateAndNormalizeWidths(ctx *renderContext) error {
|
||||||
ctx.logger.Debugf("calculateAndNormalizeWidths: Computing and normalizing widths for %d columns. Compact: %v",
|
ctx.logger.Debugf("calculateAndNormalizeWidths: Computing and normalizing widths for %d columns. Compact: %v",
|
||||||
ctx.numCols, t.config.Behavior.Compact.Merge.Enabled())
|
ctx.numCols, t.config.Behavior.Compact.Merge.Enabled())
|
||||||
|
|
||||||
// Initialize width maps
|
|
||||||
// t.headerWidths = tw.NewMapper[int, int]()
|
|
||||||
// t.rowWidths = tw.NewMapper[int, int]()
|
|
||||||
// t.footerWidths = tw.NewMapper[int, int]()
|
|
||||||
|
|
||||||
// Compute content-based widths for each section
|
// Compute content-based widths for each section
|
||||||
for _, lines := range ctx.headerLines {
|
for _, lines := range ctx.headerLines {
|
||||||
t.updateWidths(lines, t.headerWidths, t.config.Header.Padding)
|
t.updateWidths(lines, t.headerWidths, t.config.Header.Padding)
|
||||||
|
|
@ -1027,17 +1064,13 @@ func (t *Table) convertToStringer(input interface{}) ([]string, error) {
|
||||||
t.logger.Debugf("convertToString attempt %v using %v", input, t.stringer)
|
t.logger.Debugf("convertToString attempt %v using %v", input, t.stringer)
|
||||||
|
|
||||||
inputType := reflect.TypeOf(input)
|
inputType := reflect.TypeOf(input)
|
||||||
stringerFuncVal := reflect.ValueOf(t.stringer)
|
|
||||||
stringerFuncType := stringerFuncVal.Type()
|
|
||||||
|
|
||||||
// Cache lookup (simplified, actual cache logic can be more complex)
|
// Cache lookup using twcache.LRU
|
||||||
if t.stringerCacheEnabled {
|
// This assumes t.stringerCache is *twcache.LRU[reflect.Type, reflect.Value]
|
||||||
t.stringerCacheMu.RLock()
|
if t.stringerCache != nil {
|
||||||
cachedFunc, ok := t.stringerCache[inputType]
|
if cachedFunc, ok := t.stringerCache.Get(inputType); ok {
|
||||||
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)
|
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)})
|
results := cachedFunc.Call([]reflect.Value{reflect.ValueOf(input)})
|
||||||
if len(results) == 1 && results[0].Type() == reflect.TypeOf([]string{}) {
|
if len(results) == 1 && results[0].Type() == reflect.TypeOf([]string{}) {
|
||||||
return results[0].Interface().([]string), nil
|
return results[0].Interface().([]string), nil
|
||||||
|
|
@ -1045,6 +1078,9 @@ func (t *Table) convertToStringer(input interface{}) ([]string, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stringerFuncVal := reflect.ValueOf(t.stringer)
|
||||||
|
stringerFuncType := stringerFuncVal.Type()
|
||||||
|
|
||||||
// Robust type checking for the stringer function
|
// Robust type checking for the stringer function
|
||||||
validSignature := stringerFuncVal.Kind() == reflect.Func &&
|
validSignature := stringerFuncVal.Kind() == reflect.Func &&
|
||||||
stringerFuncType.NumIn() == 1 &&
|
stringerFuncType.NumIn() == 1 &&
|
||||||
|
|
@ -1068,10 +1104,6 @@ func (t *Table) convertToStringer(input interface{}) ([]string, error) {
|
||||||
}
|
}
|
||||||
} else if paramType.Kind() == reflect.Interface || (paramType.Kind() == reflect.Ptr && paramType.Elem().Kind() != reflect.Interface) {
|
} 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
|
// 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
|
assignable = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1083,7 +1115,6 @@ func (t *Table) convertToStringer(input interface{}) ([]string, error) {
|
||||||
if input == nil {
|
if input == nil {
|
||||||
// If input is nil, we must pass a zero value of the stringer's parameter type
|
// If input is nil, we must pass a zero value of the stringer's parameter type
|
||||||
// if that type is a pointer or interface.
|
// 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)}
|
callArgs = []reflect.Value{reflect.Zero(paramType)}
|
||||||
} else {
|
} else {
|
||||||
callArgs = []reflect.Value{reflect.ValueOf(input)}
|
callArgs = []reflect.Value{reflect.ValueOf(input)}
|
||||||
|
|
@ -1091,10 +1122,9 @@ func (t *Table) convertToStringer(input interface{}) ([]string, error) {
|
||||||
|
|
||||||
resultValues := stringerFuncVal.Call(callArgs)
|
resultValues := stringerFuncVal.Call(callArgs)
|
||||||
|
|
||||||
if t.stringerCacheEnabled && inputType != nil { // Only cache if inputType is valid
|
// Add to cache if enabled (not nil) and input type is valid
|
||||||
t.stringerCacheMu.Lock()
|
if t.stringerCache != nil && inputType != nil {
|
||||||
t.stringerCache[inputType] = stringerFuncVal
|
t.stringerCache.Add(inputType, stringerFuncVal)
|
||||||
t.stringerCacheMu.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return resultValues[0].Interface().([]string), nil
|
return resultValues[0].Interface().([]string), nil
|
||||||
|
|
@ -1438,7 +1468,7 @@ func (t *Table) getColMaxWidths(position tw.Position) tw.CellWidth {
|
||||||
// getEmptyColumnInfo identifies empty columns in row data.
|
// getEmptyColumnInfo identifies empty columns in row data.
|
||||||
// Parameter numOriginalCols specifies the total column count.
|
// Parameter numOriginalCols specifies the total column count.
|
||||||
// Returns a boolean slice (true for empty) and visible column count.
|
// Returns a boolean slice (true for empty) and visible column count.
|
||||||
func (t *Table) getEmptyColumnInfo(numOriginalCols int) (isEmpty []bool, visibleColCount int) {
|
func (t *Table) getEmptyColumnInfo(processedRows [][][]string, numOriginalCols int) (isEmpty []bool, visibleColCount int) {
|
||||||
isEmpty = make([]bool, numOriginalCols)
|
isEmpty = make([]bool, numOriginalCols)
|
||||||
for i := range isEmpty {
|
for i := range isEmpty {
|
||||||
isEmpty[i] = true
|
isEmpty[i] = true
|
||||||
|
|
@ -1453,9 +1483,9 @@ func (t *Table) getEmptyColumnInfo(numOriginalCols int) (isEmpty []bool, visible
|
||||||
return isEmpty, visibleColCount
|
return isEmpty, visibleColCount
|
||||||
}
|
}
|
||||||
|
|
||||||
t.logger.Debugf("getEmptyColumnInfo: Checking %d rows for %d columns...", len(t.rows), numOriginalCols)
|
t.logger.Debugf("getEmptyColumnInfo: Checking %d rows for %d columns...", len(processedRows), numOriginalCols)
|
||||||
|
|
||||||
for rowIdx, logicalRow := range t.rows {
|
for rowIdx, logicalRow := range processedRows {
|
||||||
for lineIdx, visualLine := range logicalRow {
|
for lineIdx, visualLine := range logicalRow {
|
||||||
for colIdx, cellContent := range visualLine {
|
for colIdx, cellContent := range visualLine {
|
||||||
if colIdx >= numOriginalCols {
|
if colIdx >= numOriginalCols {
|
||||||
|
|
@ -1567,15 +1597,6 @@ func (t *Table) processVariadic(elements []any) []any {
|
||||||
return elements
|
return elements
|
||||||
}
|
}
|
||||||
|
|
||||||
// toStringLines converts raw cells to formatted lines for table output
|
|
||||||
func (t *Table) toStringLines(row any, config tw.CellConfig) ([][]string, error) {
|
|
||||||
cells, err := t.convertCellsToStrings(row, config)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return t.prepareContent(cells, config), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// updateWidths updates the width map based on cell content and padding.
|
// updateWidths updates the width map based on cell content and padding.
|
||||||
// Parameters include row content, widths map, and padding configuration.
|
// Parameters include row content, widths map, and padding configuration.
|
||||||
// No return value.
|
// No return value.
|
||||||
|
|
@ -1598,10 +1619,9 @@ func (t *Table) updateWidths(row []string, widths tw.Mapper[int, int], padding t
|
||||||
lines := strings.Split(cell, tw.NewLine)
|
lines := strings.Split(cell, tw.NewLine)
|
||||||
contentWidth := 0
|
contentWidth := 0
|
||||||
for _, line := range lines {
|
for _, line := range lines {
|
||||||
|
// Always measure the raw line width, because the renderer
|
||||||
|
// will receive the raw line. Do not trim before measuring.
|
||||||
lineWidth := twwidth.Width(line)
|
lineWidth := twwidth.Width(line)
|
||||||
if t.config.Behavior.TrimSpace.Enabled() {
|
|
||||||
lineWidth = twwidth.Width(t.Trimmer(line))
|
|
||||||
}
|
|
||||||
if lineWidth > contentWidth {
|
if lineWidth > contentWidth {
|
||||||
contentWidth = lineWidth
|
contentWidth = lineWidth
|
||||||
}
|
}
|
||||||
|
|
|
||||||
137
vendor/github.com/rivo/uniseg/README.md
generated
vendored
137
vendor/github.com/rivo/uniseg/README.md
generated
vendored
|
|
@ -1,137 +0,0 @@
|
||||||
# Unicode Text Segmentation for Go
|
|
||||||
|
|
||||||
[](https://pkg.go.dev/github.com/rivo/uniseg)
|
|
||||||
[](https://goreportcard.com/report/github.com/rivo/uniseg)
|
|
||||||
|
|
||||||
This Go package implements Unicode Text Segmentation according to [Unicode Standard Annex #29](https://unicode.org/reports/tr29/), Unicode Line Breaking according to [Unicode Standard Annex #14](https://unicode.org/reports/tr14/) (Unicode version 15.0.0), and monospace font string width calculation similar to [wcwidth](https://man7.org/linux/man-pages/man3/wcwidth.3.html).
|
|
||||||
|
|
||||||
## Background
|
|
||||||
|
|
||||||
### Grapheme Clusters
|
|
||||||
|
|
||||||
In Go, [strings are read-only slices of bytes](https://go.dev/blog/strings). They can be turned into Unicode code points using the `for` loop or by casting: `[]rune(str)`. However, multiple code points may be combined into one user-perceived character or what the Unicode specification calls "grapheme cluster". Here are some examples:
|
|
||||||
|
|
||||||
|String|Bytes (UTF-8)|Code points (runes)|Grapheme clusters|
|
|
||||||
|-|-|-|-|
|
|
||||||
|Käse|6 bytes: `4b 61 cc 88 73 65`|5 code points: `4b 61 308 73 65`|4 clusters: `[4b],[61 308],[73],[65]`|
|
|
||||||
|🏳️🌈|14 bytes: `f0 9f 8f b3 ef b8 8f e2 80 8d f0 9f 8c 88`|4 code points: `1f3f3 fe0f 200d 1f308`|1 cluster: `[1f3f3 fe0f 200d 1f308]`|
|
|
||||||
|🇩🇪|8 bytes: `f0 9f 87 a9 f0 9f 87 aa`|2 code points: `1f1e9 1f1ea`|1 cluster: `[1f1e9 1f1ea]`|
|
|
||||||
|
|
||||||
This package provides tools to iterate over these grapheme clusters. This may be used to determine the number of user-perceived characters, to split strings in their intended places, or to extract individual characters which form a unit.
|
|
||||||
|
|
||||||
### Word Boundaries
|
|
||||||
|
|
||||||
Word boundaries are used in a number of different contexts. The most familiar ones are selection (double-click mouse selection), cursor movement ("move to next word" control-arrow keys), and the dialog option "Whole Word Search" for search and replace. They are also used in database queries, to determine whether elements are within a certain number of words of one another. Searching may also use word boundaries in determining matching items. This package provides tools to determine word boundaries within strings.
|
|
||||||
|
|
||||||
### Sentence Boundaries
|
|
||||||
|
|
||||||
Sentence boundaries are often used for triple-click or some other method of selecting or iterating through blocks of text that are larger than single words. They are also used to determine whether words occur within the same sentence in database queries. This package provides tools to determine sentence boundaries within strings.
|
|
||||||
|
|
||||||
### Line Breaking
|
|
||||||
|
|
||||||
Line breaking, also known as word wrapping, is the process of breaking a section of text into lines such that it will fit in the available width of a page, window or other display area. This package provides tools to determine where a string may or may not be broken and where it must be broken (for example after newline characters).
|
|
||||||
|
|
||||||
### Monospace Width
|
|
||||||
|
|
||||||
Most terminals or text displays / text editors using a monospace font (for example source code editors) use a fixed width for each character. Some characters such as emojis or characters found in Asian and other languages may take up more than one character cell. This package provides tools to determine the number of cells a string will take up when displayed in a monospace font. See [here](https://pkg.go.dev/github.com/rivo/uniseg#hdr-Monospace_Width) for more information.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go get github.com/rivo/uniseg
|
|
||||||
```
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
### Counting Characters in a String
|
|
||||||
|
|
||||||
```go
|
|
||||||
n := uniseg.GraphemeClusterCount("🇩🇪🏳️🌈")
|
|
||||||
fmt.Println(n)
|
|
||||||
// 2
|
|
||||||
```
|
|
||||||
|
|
||||||
### Calculating the Monospace String Width
|
|
||||||
|
|
||||||
```go
|
|
||||||
width := uniseg.StringWidth("🇩🇪🏳️🌈!")
|
|
||||||
fmt.Println(width)
|
|
||||||
// 5
|
|
||||||
```
|
|
||||||
|
|
||||||
### Using the [`Graphemes`](https://pkg.go.dev/github.com/rivo/uniseg#Graphemes) Class
|
|
||||||
|
|
||||||
This is the most convenient method of iterating over grapheme clusters:
|
|
||||||
|
|
||||||
```go
|
|
||||||
gr := uniseg.NewGraphemes("👍🏼!")
|
|
||||||
for gr.Next() {
|
|
||||||
fmt.Printf("%x ", gr.Runes())
|
|
||||||
}
|
|
||||||
// [1f44d 1f3fc] [21]
|
|
||||||
```
|
|
||||||
|
|
||||||
### Using the [`Step`](https://pkg.go.dev/github.com/rivo/uniseg#Step) or [`StepString`](https://pkg.go.dev/github.com/rivo/uniseg#StepString) Function
|
|
||||||
|
|
||||||
This avoids allocating a new `Graphemes` object but it requires the handling of states and boundaries:
|
|
||||||
|
|
||||||
```go
|
|
||||||
str := "🇩🇪🏳️🌈"
|
|
||||||
state := -1
|
|
||||||
var c string
|
|
||||||
for len(str) > 0 {
|
|
||||||
c, str, _, state = uniseg.StepString(str, state)
|
|
||||||
fmt.Printf("%x ", []rune(c))
|
|
||||||
}
|
|
||||||
// [1f1e9 1f1ea] [1f3f3 fe0f 200d 1f308]
|
|
||||||
```
|
|
||||||
|
|
||||||
### Advanced Examples
|
|
||||||
|
|
||||||
The [`Graphemes`](https://pkg.go.dev/github.com/rivo/uniseg#Graphemes) class offers the most convenient way to access all functionality of this package. But in some cases, it may be better to use the specialized functions directly. For example, if you're only interested in word segmentation, use [`FirstWord`](https://pkg.go.dev/github.com/rivo/uniseg#FirstWord) or [`FirstWordInString`](https://pkg.go.dev/github.com/rivo/uniseg#FirstWordInString):
|
|
||||||
|
|
||||||
```go
|
|
||||||
str := "Hello, world!"
|
|
||||||
state := -1
|
|
||||||
var c string
|
|
||||||
for len(str) > 0 {
|
|
||||||
c, str, state = uniseg.FirstWordInString(str, state)
|
|
||||||
fmt.Printf("(%s)\n", c)
|
|
||||||
}
|
|
||||||
// (Hello)
|
|
||||||
// (,)
|
|
||||||
// ( )
|
|
||||||
// (world)
|
|
||||||
// (!)
|
|
||||||
```
|
|
||||||
|
|
||||||
Similarly, use
|
|
||||||
|
|
||||||
- [`FirstGraphemeCluster`](https://pkg.go.dev/github.com/rivo/uniseg#FirstGraphemeCluster) or [`FirstGraphemeClusterInString`](https://pkg.go.dev/github.com/rivo/uniseg#FirstGraphemeClusterInString) for grapheme cluster determination only,
|
|
||||||
- [`FirstSentence`](https://pkg.go.dev/github.com/rivo/uniseg#FirstSentence) or [`FirstSentenceInString`](https://pkg.go.dev/github.com/rivo/uniseg#FirstSentenceInString) for sentence segmentation only, and
|
|
||||||
- [`FirstLineSegment`](https://pkg.go.dev/github.com/rivo/uniseg#FirstLineSegment) or [`FirstLineSegmentInString`](https://pkg.go.dev/github.com/rivo/uniseg#FirstLineSegmentInString) for line breaking / word wrapping (although using [`Step`](https://pkg.go.dev/github.com/rivo/uniseg#Step) or [`StepString`](https://pkg.go.dev/github.com/rivo/uniseg#StepString) is preferred as it will observe grapheme cluster boundaries).
|
|
||||||
|
|
||||||
If you're only interested in the width of characters, use [`FirstGraphemeCluster`](https://pkg.go.dev/github.com/rivo/uniseg#FirstGraphemeCluster) or [`FirstGraphemeClusterInString`](https://pkg.go.dev/github.com/rivo/uniseg#FirstGraphemeClusterInString). It is much faster than using [`Step`](https://pkg.go.dev/github.com/rivo/uniseg#Step), [`StepString`](https://pkg.go.dev/github.com/rivo/uniseg#StepString), or the [`Graphemes`](https://pkg.go.dev/github.com/rivo/uniseg#Graphemes) class because it does not include the logic for word / sentence / line boundaries.
|
|
||||||
|
|
||||||
Finally, if you need to reverse a string while preserving grapheme clusters, use [`ReverseString`](https://pkg.go.dev/github.com/rivo/uniseg#ReverseString):
|
|
||||||
|
|
||||||
```go
|
|
||||||
fmt.Println(uniseg.ReverseString("🇩🇪🏳️🌈"))
|
|
||||||
// 🏳️🌈🇩🇪
|
|
||||||
```
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
Refer to https://pkg.go.dev/github.com/rivo/uniseg for the package's documentation.
|
|
||||||
|
|
||||||
## Dependencies
|
|
||||||
|
|
||||||
This package does not depend on any packages outside the standard library.
|
|
||||||
|
|
||||||
## Sponsor this Project
|
|
||||||
|
|
||||||
[Become a Sponsor on GitHub](https://github.com/sponsors/rivo?metadata_source=uniseg_readme) to support this project!
|
|
||||||
|
|
||||||
## Your Feedback
|
|
||||||
|
|
||||||
Add your issue here on GitHub, preferably before submitting any PR's. Feel free to get in touch if you have any questions.
|
|
||||||
108
vendor/github.com/rivo/uniseg/doc.go
generated
vendored
108
vendor/github.com/rivo/uniseg/doc.go
generated
vendored
|
|
@ -1,108 +0,0 @@
|
||||||
/*
|
|
||||||
Package uniseg implements Unicode Text Segmentation, Unicode Line Breaking, and
|
|
||||||
string width calculation for monospace fonts. Unicode Text Segmentation conforms
|
|
||||||
to Unicode Standard Annex #29 (https://unicode.org/reports/tr29/) and Unicode
|
|
||||||
Line Breaking conforms to Unicode Standard Annex #14
|
|
||||||
(https://unicode.org/reports/tr14/).
|
|
||||||
|
|
||||||
In short, using this package, you can split a string into grapheme clusters
|
|
||||||
(what people would usually refer to as a "character"), into words, and into
|
|
||||||
sentences. Or, in its simplest case, this package allows you to count the number
|
|
||||||
of characters in a string, especially when it contains complex characters such
|
|
||||||
as emojis, combining characters, or characters from Asian, Arabic, Hebrew, or
|
|
||||||
other languages. Additionally, you can use it to implement line breaking (or
|
|
||||||
"word wrapping"), that is, to determine where text can be broken over to the
|
|
||||||
next line when the width of the line is not big enough to fit the entire text.
|
|
||||||
Finally, you can use it to calculate the display width of a string for monospace
|
|
||||||
fonts.
|
|
||||||
|
|
||||||
# Getting Started
|
|
||||||
|
|
||||||
If you just want to count the number of characters in a string, you can use
|
|
||||||
[GraphemeClusterCount]. If you want to determine the display width of a string,
|
|
||||||
you can use [StringWidth]. If you want to iterate over a string, you can use
|
|
||||||
[Step], [StepString], or the [Graphemes] class (more convenient but less
|
|
||||||
performant). This will provide you with all information: grapheme clusters,
|
|
||||||
word boundaries, sentence boundaries, line breaks, and monospace character
|
|
||||||
widths. The specialized functions [FirstGraphemeCluster],
|
|
||||||
[FirstGraphemeClusterInString], [FirstWord], [FirstWordInString],
|
|
||||||
[FirstSentence], and [FirstSentenceInString] can be used if only one type of
|
|
||||||
information is needed.
|
|
||||||
|
|
||||||
# Grapheme Clusters
|
|
||||||
|
|
||||||
Consider the rainbow flag emoji: 🏳️🌈. On most modern systems, it appears as one
|
|
||||||
character. But its string representation actually has 14 bytes, so counting
|
|
||||||
bytes (or using len("🏳️🌈")) will not work as expected. Counting runes won't,
|
|
||||||
either: The flag has 4 Unicode code points, thus 4 runes. The stdlib function
|
|
||||||
utf8.RuneCountInString("🏳️🌈") and len([]rune("🏳️🌈")) will both return 4.
|
|
||||||
|
|
||||||
The [GraphemeClusterCount] function will return 1 for the rainbow flag emoji.
|
|
||||||
The Graphemes class and a variety of functions in this package will allow you to
|
|
||||||
split strings into its grapheme clusters.
|
|
||||||
|
|
||||||
# Word Boundaries
|
|
||||||
|
|
||||||
Word boundaries are used in a number of different contexts. The most familiar
|
|
||||||
ones are selection (double-click mouse selection), cursor movement ("move to
|
|
||||||
next word" control-arrow keys), and the dialog option "Whole Word Search" for
|
|
||||||
search and replace. This package provides methods for determining word
|
|
||||||
boundaries.
|
|
||||||
|
|
||||||
# Sentence Boundaries
|
|
||||||
|
|
||||||
Sentence boundaries are often used for triple-click or some other method of
|
|
||||||
selecting or iterating through blocks of text that are larger than single words.
|
|
||||||
They are also used to determine whether words occur within the same sentence in
|
|
||||||
database queries. This package provides methods for determining sentence
|
|
||||||
boundaries.
|
|
||||||
|
|
||||||
# Line Breaking
|
|
||||||
|
|
||||||
Line breaking, also known as word wrapping, is the process of breaking a section
|
|
||||||
of text into lines such that it will fit in the available width of a page,
|
|
||||||
window or other display area. This package provides methods to determine the
|
|
||||||
positions in a string where a line must be broken, may be broken, or must not be
|
|
||||||
broken.
|
|
||||||
|
|
||||||
# Monospace Width
|
|
||||||
|
|
||||||
Monospace width, as referred to in this package, is the width of a string in a
|
|
||||||
monospace font. This is commonly used in terminal user interfaces or text
|
|
||||||
displays or editors that don't support proportional fonts. A width of 1
|
|
||||||
corresponds to a single character cell. The C function [wcswidth()] and its
|
|
||||||
implementation in other programming languages is in widespread use for the same
|
|
||||||
purpose. However, there is no standard for the calculation of such widths, and
|
|
||||||
this package differs from wcswidth() in a number of ways, presumably to generate
|
|
||||||
more visually pleasing results.
|
|
||||||
|
|
||||||
To start, we assume that every code point has a width of 1, with the following
|
|
||||||
exceptions:
|
|
||||||
|
|
||||||
- Code points with grapheme cluster break properties Control, CR, LF, Extend,
|
|
||||||
and ZWJ have a width of 0.
|
|
||||||
- U+2E3A, Two-Em Dash, has a width of 3.
|
|
||||||
- U+2E3B, Three-Em Dash, has a width of 4.
|
|
||||||
- Characters with the East-Asian Width properties "Fullwidth" (F) and "Wide"
|
|
||||||
(W) have a width of 2. (Properties "Ambiguous" (A) and "Neutral" (N) both
|
|
||||||
have a width of 1.)
|
|
||||||
- Code points with grapheme cluster break property Regional Indicator have a
|
|
||||||
width of 2.
|
|
||||||
- Code points with grapheme cluster break property Extended Pictographic have
|
|
||||||
a width of 2, unless their Emoji Presentation flag is "No", in which case
|
|
||||||
the width is 1.
|
|
||||||
|
|
||||||
For Hangul grapheme clusters composed of conjoining Jamo and for Regional
|
|
||||||
Indicators (flags), all code points except the first one have a width of 0. For
|
|
||||||
grapheme clusters starting with an Extended Pictographic, any additional code
|
|
||||||
point will force a total width of 2, except if the Variation Selector-15
|
|
||||||
(U+FE0E) is included, in which case the total width is always 1. Grapheme
|
|
||||||
clusters ending with Variation Selector-16 (U+FE0F) have a width of 2.
|
|
||||||
|
|
||||||
Note that whether these widths appear correct depends on your application's
|
|
||||||
render engine, to which extent it conforms to the Unicode Standard, and its
|
|
||||||
choice of font.
|
|
||||||
|
|
||||||
[wcswidth()]: https://man7.org/linux/man-pages/man3/wcswidth.3.html
|
|
||||||
*/
|
|
||||||
package uniseg
|
|
||||||
2588
vendor/github.com/rivo/uniseg/eastasianwidth.go
generated
vendored
2588
vendor/github.com/rivo/uniseg/eastasianwidth.go
generated
vendored
File diff suppressed because it is too large
Load diff
295
vendor/github.com/rivo/uniseg/emojipresentation.go
generated
vendored
295
vendor/github.com/rivo/uniseg/emojipresentation.go
generated
vendored
|
|
@ -1,295 +0,0 @@
|
||||||
// Code generated via go generate from gen_properties.go. DO NOT EDIT.
|
|
||||||
|
|
||||||
package uniseg
|
|
||||||
|
|
||||||
// emojiPresentation are taken from
|
|
||||||
//
|
|
||||||
// and
|
|
||||||
// https://unicode.org/Public/15.0.0/ucd/emoji/emoji-data.txt
|
|
||||||
// ("Extended_Pictographic" only)
|
|
||||||
// on September 5, 2023. See https://www.unicode.org/license.html for the Unicode
|
|
||||||
// license agreement.
|
|
||||||
var emojiPresentation = [][3]int{
|
|
||||||
{0x231A, 0x231B, prEmojiPresentation}, // E0.6 [2] (⌚..⌛) watch..hourglass done
|
|
||||||
{0x23E9, 0x23EC, prEmojiPresentation}, // E0.6 [4] (⏩..⏬) fast-forward button..fast down button
|
|
||||||
{0x23F0, 0x23F0, prEmojiPresentation}, // E0.6 [1] (⏰) alarm clock
|
|
||||||
{0x23F3, 0x23F3, prEmojiPresentation}, // E0.6 [1] (⏳) hourglass not done
|
|
||||||
{0x25FD, 0x25FE, prEmojiPresentation}, // E0.6 [2] (◽..◾) white medium-small square..black medium-small square
|
|
||||||
{0x2614, 0x2615, prEmojiPresentation}, // E0.6 [2] (☔..☕) umbrella with rain drops..hot beverage
|
|
||||||
{0x2648, 0x2653, prEmojiPresentation}, // E0.6 [12] (♈..♓) Aries..Pisces
|
|
||||||
{0x267F, 0x267F, prEmojiPresentation}, // E0.6 [1] (♿) wheelchair symbol
|
|
||||||
{0x2693, 0x2693, prEmojiPresentation}, // E0.6 [1] (⚓) anchor
|
|
||||||
{0x26A1, 0x26A1, prEmojiPresentation}, // E0.6 [1] (⚡) high voltage
|
|
||||||
{0x26AA, 0x26AB, prEmojiPresentation}, // E0.6 [2] (⚪..⚫) white circle..black circle
|
|
||||||
{0x26BD, 0x26BE, prEmojiPresentation}, // E0.6 [2] (⚽..⚾) soccer ball..baseball
|
|
||||||
{0x26C4, 0x26C5, prEmojiPresentation}, // E0.6 [2] (⛄..⛅) snowman without snow..sun behind cloud
|
|
||||||
{0x26CE, 0x26CE, prEmojiPresentation}, // E0.6 [1] (⛎) Ophiuchus
|
|
||||||
{0x26D4, 0x26D4, prEmojiPresentation}, // E0.6 [1] (⛔) no entry
|
|
||||||
{0x26EA, 0x26EA, prEmojiPresentation}, // E0.6 [1] (⛪) church
|
|
||||||
{0x26F2, 0x26F3, prEmojiPresentation}, // E0.6 [2] (⛲..⛳) fountain..flag in hole
|
|
||||||
{0x26F5, 0x26F5, prEmojiPresentation}, // E0.6 [1] (⛵) sailboat
|
|
||||||
{0x26FA, 0x26FA, prEmojiPresentation}, // E0.6 [1] (⛺) tent
|
|
||||||
{0x26FD, 0x26FD, prEmojiPresentation}, // E0.6 [1] (⛽) fuel pump
|
|
||||||
{0x2705, 0x2705, prEmojiPresentation}, // E0.6 [1] (✅) check mark button
|
|
||||||
{0x270A, 0x270B, prEmojiPresentation}, // E0.6 [2] (✊..✋) raised fist..raised hand
|
|
||||||
{0x2728, 0x2728, prEmojiPresentation}, // E0.6 [1] (✨) sparkles
|
|
||||||
{0x274C, 0x274C, prEmojiPresentation}, // E0.6 [1] (❌) cross mark
|
|
||||||
{0x274E, 0x274E, prEmojiPresentation}, // E0.6 [1] (❎) cross mark button
|
|
||||||
{0x2753, 0x2755, prEmojiPresentation}, // E0.6 [3] (❓..❕) red question mark..white exclamation mark
|
|
||||||
{0x2757, 0x2757, prEmojiPresentation}, // E0.6 [1] (❗) red exclamation mark
|
|
||||||
{0x2795, 0x2797, prEmojiPresentation}, // E0.6 [3] (➕..➗) plus..divide
|
|
||||||
{0x27B0, 0x27B0, prEmojiPresentation}, // E0.6 [1] (➰) curly loop
|
|
||||||
{0x27BF, 0x27BF, prEmojiPresentation}, // E1.0 [1] (➿) double curly loop
|
|
||||||
{0x2B1B, 0x2B1C, prEmojiPresentation}, // E0.6 [2] (⬛..⬜) black large square..white large square
|
|
||||||
{0x2B50, 0x2B50, prEmojiPresentation}, // E0.6 [1] (⭐) star
|
|
||||||
{0x2B55, 0x2B55, prEmojiPresentation}, // E0.6 [1] (⭕) hollow red circle
|
|
||||||
{0x1F004, 0x1F004, prEmojiPresentation}, // E0.6 [1] (🀄) mahjong red dragon
|
|
||||||
{0x1F0CF, 0x1F0CF, prEmojiPresentation}, // E0.6 [1] (🃏) joker
|
|
||||||
{0x1F18E, 0x1F18E, prEmojiPresentation}, // E0.6 [1] (🆎) AB button (blood type)
|
|
||||||
{0x1F191, 0x1F19A, prEmojiPresentation}, // E0.6 [10] (🆑..🆚) CL button..VS button
|
|
||||||
{0x1F1E6, 0x1F1FF, prEmojiPresentation}, // E0.0 [26] (🇦..🇿) regional indicator symbol letter a..regional indicator symbol letter z
|
|
||||||
{0x1F201, 0x1F201, prEmojiPresentation}, // E0.6 [1] (🈁) Japanese “here” button
|
|
||||||
{0x1F21A, 0x1F21A, prEmojiPresentation}, // E0.6 [1] (🈚) Japanese “free of charge” button
|
|
||||||
{0x1F22F, 0x1F22F, prEmojiPresentation}, // E0.6 [1] (🈯) Japanese “reserved” button
|
|
||||||
{0x1F232, 0x1F236, prEmojiPresentation}, // E0.6 [5] (🈲..🈶) Japanese “prohibited” button..Japanese “not free of charge” button
|
|
||||||
{0x1F238, 0x1F23A, prEmojiPresentation}, // E0.6 [3] (🈸..🈺) Japanese “application” button..Japanese “open for business” button
|
|
||||||
{0x1F250, 0x1F251, prEmojiPresentation}, // E0.6 [2] (🉐..🉑) Japanese “bargain” button..Japanese “acceptable” button
|
|
||||||
{0x1F300, 0x1F30C, prEmojiPresentation}, // E0.6 [13] (🌀..🌌) cyclone..milky way
|
|
||||||
{0x1F30D, 0x1F30E, prEmojiPresentation}, // E0.7 [2] (🌍..🌎) globe showing Europe-Africa..globe showing Americas
|
|
||||||
{0x1F30F, 0x1F30F, prEmojiPresentation}, // E0.6 [1] (🌏) globe showing Asia-Australia
|
|
||||||
{0x1F310, 0x1F310, prEmojiPresentation}, // E1.0 [1] (🌐) globe with meridians
|
|
||||||
{0x1F311, 0x1F311, prEmojiPresentation}, // E0.6 [1] (🌑) new moon
|
|
||||||
{0x1F312, 0x1F312, prEmojiPresentation}, // E1.0 [1] (🌒) waxing crescent moon
|
|
||||||
{0x1F313, 0x1F315, prEmojiPresentation}, // E0.6 [3] (🌓..🌕) first quarter moon..full moon
|
|
||||||
{0x1F316, 0x1F318, prEmojiPresentation}, // E1.0 [3] (🌖..🌘) waning gibbous moon..waning crescent moon
|
|
||||||
{0x1F319, 0x1F319, prEmojiPresentation}, // E0.6 [1] (🌙) crescent moon
|
|
||||||
{0x1F31A, 0x1F31A, prEmojiPresentation}, // E1.0 [1] (🌚) new moon face
|
|
||||||
{0x1F31B, 0x1F31B, prEmojiPresentation}, // E0.6 [1] (🌛) first quarter moon face
|
|
||||||
{0x1F31C, 0x1F31C, prEmojiPresentation}, // E0.7 [1] (🌜) last quarter moon face
|
|
||||||
{0x1F31D, 0x1F31E, prEmojiPresentation}, // E1.0 [2] (🌝..🌞) full moon face..sun with face
|
|
||||||
{0x1F31F, 0x1F320, prEmojiPresentation}, // E0.6 [2] (🌟..🌠) glowing star..shooting star
|
|
||||||
{0x1F32D, 0x1F32F, prEmojiPresentation}, // E1.0 [3] (🌭..🌯) hot dog..burrito
|
|
||||||
{0x1F330, 0x1F331, prEmojiPresentation}, // E0.6 [2] (🌰..🌱) chestnut..seedling
|
|
||||||
{0x1F332, 0x1F333, prEmojiPresentation}, // E1.0 [2] (🌲..🌳) evergreen tree..deciduous tree
|
|
||||||
{0x1F334, 0x1F335, prEmojiPresentation}, // E0.6 [2] (🌴..🌵) palm tree..cactus
|
|
||||||
{0x1F337, 0x1F34A, prEmojiPresentation}, // E0.6 [20] (🌷..🍊) tulip..tangerine
|
|
||||||
{0x1F34B, 0x1F34B, prEmojiPresentation}, // E1.0 [1] (🍋) lemon
|
|
||||||
{0x1F34C, 0x1F34F, prEmojiPresentation}, // E0.6 [4] (🍌..🍏) banana..green apple
|
|
||||||
{0x1F350, 0x1F350, prEmojiPresentation}, // E1.0 [1] (🍐) pear
|
|
||||||
{0x1F351, 0x1F37B, prEmojiPresentation}, // E0.6 [43] (🍑..🍻) peach..clinking beer mugs
|
|
||||||
{0x1F37C, 0x1F37C, prEmojiPresentation}, // E1.0 [1] (🍼) baby bottle
|
|
||||||
{0x1F37E, 0x1F37F, prEmojiPresentation}, // E1.0 [2] (🍾..🍿) bottle with popping cork..popcorn
|
|
||||||
{0x1F380, 0x1F393, prEmojiPresentation}, // E0.6 [20] (🎀..🎓) ribbon..graduation cap
|
|
||||||
{0x1F3A0, 0x1F3C4, prEmojiPresentation}, // E0.6 [37] (🎠..🏄) carousel horse..person surfing
|
|
||||||
{0x1F3C5, 0x1F3C5, prEmojiPresentation}, // E1.0 [1] (🏅) sports medal
|
|
||||||
{0x1F3C6, 0x1F3C6, prEmojiPresentation}, // E0.6 [1] (🏆) trophy
|
|
||||||
{0x1F3C7, 0x1F3C7, prEmojiPresentation}, // E1.0 [1] (🏇) horse racing
|
|
||||||
{0x1F3C8, 0x1F3C8, prEmojiPresentation}, // E0.6 [1] (🏈) american football
|
|
||||||
{0x1F3C9, 0x1F3C9, prEmojiPresentation}, // E1.0 [1] (🏉) rugby football
|
|
||||||
{0x1F3CA, 0x1F3CA, prEmojiPresentation}, // E0.6 [1] (🏊) person swimming
|
|
||||||
{0x1F3CF, 0x1F3D3, prEmojiPresentation}, // E1.0 [5] (🏏..🏓) cricket game..ping pong
|
|
||||||
{0x1F3E0, 0x1F3E3, prEmojiPresentation}, // E0.6 [4] (🏠..🏣) house..Japanese post office
|
|
||||||
{0x1F3E4, 0x1F3E4, prEmojiPresentation}, // E1.0 [1] (🏤) post office
|
|
||||||
{0x1F3E5, 0x1F3F0, prEmojiPresentation}, // E0.6 [12] (🏥..🏰) hospital..castle
|
|
||||||
{0x1F3F4, 0x1F3F4, prEmojiPresentation}, // E1.0 [1] (🏴) black flag
|
|
||||||
{0x1F3F8, 0x1F407, prEmojiPresentation}, // E1.0 [16] (🏸..🐇) badminton..rabbit
|
|
||||||
{0x1F408, 0x1F408, prEmojiPresentation}, // E0.7 [1] (🐈) cat
|
|
||||||
{0x1F409, 0x1F40B, prEmojiPresentation}, // E1.0 [3] (🐉..🐋) dragon..whale
|
|
||||||
{0x1F40C, 0x1F40E, prEmojiPresentation}, // E0.6 [3] (🐌..🐎) snail..horse
|
|
||||||
{0x1F40F, 0x1F410, prEmojiPresentation}, // E1.0 [2] (🐏..🐐) ram..goat
|
|
||||||
{0x1F411, 0x1F412, prEmojiPresentation}, // E0.6 [2] (🐑..🐒) ewe..monkey
|
|
||||||
{0x1F413, 0x1F413, prEmojiPresentation}, // E1.0 [1] (🐓) rooster
|
|
||||||
{0x1F414, 0x1F414, prEmojiPresentation}, // E0.6 [1] (🐔) chicken
|
|
||||||
{0x1F415, 0x1F415, prEmojiPresentation}, // E0.7 [1] (🐕) dog
|
|
||||||
{0x1F416, 0x1F416, prEmojiPresentation}, // E1.0 [1] (🐖) pig
|
|
||||||
{0x1F417, 0x1F429, prEmojiPresentation}, // E0.6 [19] (🐗..🐩) boar..poodle
|
|
||||||
{0x1F42A, 0x1F42A, prEmojiPresentation}, // E1.0 [1] (🐪) camel
|
|
||||||
{0x1F42B, 0x1F43E, prEmojiPresentation}, // E0.6 [20] (🐫..🐾) two-hump camel..paw prints
|
|
||||||
{0x1F440, 0x1F440, prEmojiPresentation}, // E0.6 [1] (👀) eyes
|
|
||||||
{0x1F442, 0x1F464, prEmojiPresentation}, // E0.6 [35] (👂..👤) ear..bust in silhouette
|
|
||||||
{0x1F465, 0x1F465, prEmojiPresentation}, // E1.0 [1] (👥) busts in silhouette
|
|
||||||
{0x1F466, 0x1F46B, prEmojiPresentation}, // E0.6 [6] (👦..👫) boy..woman and man holding hands
|
|
||||||
{0x1F46C, 0x1F46D, prEmojiPresentation}, // E1.0 [2] (👬..👭) men holding hands..women holding hands
|
|
||||||
{0x1F46E, 0x1F4AC, prEmojiPresentation}, // E0.6 [63] (👮..💬) police officer..speech balloon
|
|
||||||
{0x1F4AD, 0x1F4AD, prEmojiPresentation}, // E1.0 [1] (💭) thought balloon
|
|
||||||
{0x1F4AE, 0x1F4B5, prEmojiPresentation}, // E0.6 [8] (💮..💵) white flower..dollar banknote
|
|
||||||
{0x1F4B6, 0x1F4B7, prEmojiPresentation}, // E1.0 [2] (💶..💷) euro banknote..pound banknote
|
|
||||||
{0x1F4B8, 0x1F4EB, prEmojiPresentation}, // E0.6 [52] (💸..📫) money with wings..closed mailbox with raised flag
|
|
||||||
{0x1F4EC, 0x1F4ED, prEmojiPresentation}, // E0.7 [2] (📬..📭) open mailbox with raised flag..open mailbox with lowered flag
|
|
||||||
{0x1F4EE, 0x1F4EE, prEmojiPresentation}, // E0.6 [1] (📮) postbox
|
|
||||||
{0x1F4EF, 0x1F4EF, prEmojiPresentation}, // E1.0 [1] (📯) postal horn
|
|
||||||
{0x1F4F0, 0x1F4F4, prEmojiPresentation}, // E0.6 [5] (📰..📴) newspaper..mobile phone off
|
|
||||||
{0x1F4F5, 0x1F4F5, prEmojiPresentation}, // E1.0 [1] (📵) no mobile phones
|
|
||||||
{0x1F4F6, 0x1F4F7, prEmojiPresentation}, // E0.6 [2] (📶..📷) antenna bars..camera
|
|
||||||
{0x1F4F8, 0x1F4F8, prEmojiPresentation}, // E1.0 [1] (📸) camera with flash
|
|
||||||
{0x1F4F9, 0x1F4FC, prEmojiPresentation}, // E0.6 [4] (📹..📼) video camera..videocassette
|
|
||||||
{0x1F4FF, 0x1F502, prEmojiPresentation}, // E1.0 [4] (📿..🔂) prayer beads..repeat single button
|
|
||||||
{0x1F503, 0x1F503, prEmojiPresentation}, // E0.6 [1] (🔃) clockwise vertical arrows
|
|
||||||
{0x1F504, 0x1F507, prEmojiPresentation}, // E1.0 [4] (🔄..🔇) counterclockwise arrows button..muted speaker
|
|
||||||
{0x1F508, 0x1F508, prEmojiPresentation}, // E0.7 [1] (🔈) speaker low volume
|
|
||||||
{0x1F509, 0x1F509, prEmojiPresentation}, // E1.0 [1] (🔉) speaker medium volume
|
|
||||||
{0x1F50A, 0x1F514, prEmojiPresentation}, // E0.6 [11] (🔊..🔔) speaker high volume..bell
|
|
||||||
{0x1F515, 0x1F515, prEmojiPresentation}, // E1.0 [1] (🔕) bell with slash
|
|
||||||
{0x1F516, 0x1F52B, prEmojiPresentation}, // E0.6 [22] (🔖..🔫) bookmark..water pistol
|
|
||||||
{0x1F52C, 0x1F52D, prEmojiPresentation}, // E1.0 [2] (🔬..🔭) microscope..telescope
|
|
||||||
{0x1F52E, 0x1F53D, prEmojiPresentation}, // E0.6 [16] (🔮..🔽) crystal ball..downwards button
|
|
||||||
{0x1F54B, 0x1F54E, prEmojiPresentation}, // E1.0 [4] (🕋..🕎) kaaba..menorah
|
|
||||||
{0x1F550, 0x1F55B, prEmojiPresentation}, // E0.6 [12] (🕐..🕛) one o’clock..twelve o’clock
|
|
||||||
{0x1F55C, 0x1F567, prEmojiPresentation}, // E0.7 [12] (🕜..🕧) one-thirty..twelve-thirty
|
|
||||||
{0x1F57A, 0x1F57A, prEmojiPresentation}, // E3.0 [1] (🕺) man dancing
|
|
||||||
{0x1F595, 0x1F596, prEmojiPresentation}, // E1.0 [2] (🖕..🖖) middle finger..vulcan salute
|
|
||||||
{0x1F5A4, 0x1F5A4, prEmojiPresentation}, // E3.0 [1] (🖤) black heart
|
|
||||||
{0x1F5FB, 0x1F5FF, prEmojiPresentation}, // E0.6 [5] (🗻..🗿) mount fuji..moai
|
|
||||||
{0x1F600, 0x1F600, prEmojiPresentation}, // E1.0 [1] (😀) grinning face
|
|
||||||
{0x1F601, 0x1F606, prEmojiPresentation}, // E0.6 [6] (😁..😆) beaming face with smiling eyes..grinning squinting face
|
|
||||||
{0x1F607, 0x1F608, prEmojiPresentation}, // E1.0 [2] (😇..😈) smiling face with halo..smiling face with horns
|
|
||||||
{0x1F609, 0x1F60D, prEmojiPresentation}, // E0.6 [5] (😉..😍) winking face..smiling face with heart-eyes
|
|
||||||
{0x1F60E, 0x1F60E, prEmojiPresentation}, // E1.0 [1] (😎) smiling face with sunglasses
|
|
||||||
{0x1F60F, 0x1F60F, prEmojiPresentation}, // E0.6 [1] (😏) smirking face
|
|
||||||
{0x1F610, 0x1F610, prEmojiPresentation}, // E0.7 [1] (😐) neutral face
|
|
||||||
{0x1F611, 0x1F611, prEmojiPresentation}, // E1.0 [1] (😑) expressionless face
|
|
||||||
{0x1F612, 0x1F614, prEmojiPresentation}, // E0.6 [3] (😒..😔) unamused face..pensive face
|
|
||||||
{0x1F615, 0x1F615, prEmojiPresentation}, // E1.0 [1] (😕) confused face
|
|
||||||
{0x1F616, 0x1F616, prEmojiPresentation}, // E0.6 [1] (😖) confounded face
|
|
||||||
{0x1F617, 0x1F617, prEmojiPresentation}, // E1.0 [1] (😗) kissing face
|
|
||||||
{0x1F618, 0x1F618, prEmojiPresentation}, // E0.6 [1] (😘) face blowing a kiss
|
|
||||||
{0x1F619, 0x1F619, prEmojiPresentation}, // E1.0 [1] (😙) kissing face with smiling eyes
|
|
||||||
{0x1F61A, 0x1F61A, prEmojiPresentation}, // E0.6 [1] (😚) kissing face with closed eyes
|
|
||||||
{0x1F61B, 0x1F61B, prEmojiPresentation}, // E1.0 [1] (😛) face with tongue
|
|
||||||
{0x1F61C, 0x1F61E, prEmojiPresentation}, // E0.6 [3] (😜..😞) winking face with tongue..disappointed face
|
|
||||||
{0x1F61F, 0x1F61F, prEmojiPresentation}, // E1.0 [1] (😟) worried face
|
|
||||||
{0x1F620, 0x1F625, prEmojiPresentation}, // E0.6 [6] (😠..😥) angry face..sad but relieved face
|
|
||||||
{0x1F626, 0x1F627, prEmojiPresentation}, // E1.0 [2] (😦..😧) frowning face with open mouth..anguished face
|
|
||||||
{0x1F628, 0x1F62B, prEmojiPresentation}, // E0.6 [4] (😨..😫) fearful face..tired face
|
|
||||||
{0x1F62C, 0x1F62C, prEmojiPresentation}, // E1.0 [1] (😬) grimacing face
|
|
||||||
{0x1F62D, 0x1F62D, prEmojiPresentation}, // E0.6 [1] (😭) loudly crying face
|
|
||||||
{0x1F62E, 0x1F62F, prEmojiPresentation}, // E1.0 [2] (😮..😯) face with open mouth..hushed face
|
|
||||||
{0x1F630, 0x1F633, prEmojiPresentation}, // E0.6 [4] (😰..😳) anxious face with sweat..flushed face
|
|
||||||
{0x1F634, 0x1F634, prEmojiPresentation}, // E1.0 [1] (😴) sleeping face
|
|
||||||
{0x1F635, 0x1F635, prEmojiPresentation}, // E0.6 [1] (😵) face with crossed-out eyes
|
|
||||||
{0x1F636, 0x1F636, prEmojiPresentation}, // E1.0 [1] (😶) face without mouth
|
|
||||||
{0x1F637, 0x1F640, prEmojiPresentation}, // E0.6 [10] (😷..🙀) face with medical mask..weary cat
|
|
||||||
{0x1F641, 0x1F644, prEmojiPresentation}, // E1.0 [4] (🙁..🙄) slightly frowning face..face with rolling eyes
|
|
||||||
{0x1F645, 0x1F64F, prEmojiPresentation}, // E0.6 [11] (🙅..🙏) person gesturing NO..folded hands
|
|
||||||
{0x1F680, 0x1F680, prEmojiPresentation}, // E0.6 [1] (🚀) rocket
|
|
||||||
{0x1F681, 0x1F682, prEmojiPresentation}, // E1.0 [2] (🚁..🚂) helicopter..locomotive
|
|
||||||
{0x1F683, 0x1F685, prEmojiPresentation}, // E0.6 [3] (🚃..🚅) railway car..bullet train
|
|
||||||
{0x1F686, 0x1F686, prEmojiPresentation}, // E1.0 [1] (🚆) train
|
|
||||||
{0x1F687, 0x1F687, prEmojiPresentation}, // E0.6 [1] (🚇) metro
|
|
||||||
{0x1F688, 0x1F688, prEmojiPresentation}, // E1.0 [1] (🚈) light rail
|
|
||||||
{0x1F689, 0x1F689, prEmojiPresentation}, // E0.6 [1] (🚉) station
|
|
||||||
{0x1F68A, 0x1F68B, prEmojiPresentation}, // E1.0 [2] (🚊..🚋) tram..tram car
|
|
||||||
{0x1F68C, 0x1F68C, prEmojiPresentation}, // E0.6 [1] (🚌) bus
|
|
||||||
{0x1F68D, 0x1F68D, prEmojiPresentation}, // E0.7 [1] (🚍) oncoming bus
|
|
||||||
{0x1F68E, 0x1F68E, prEmojiPresentation}, // E1.0 [1] (🚎) trolleybus
|
|
||||||
{0x1F68F, 0x1F68F, prEmojiPresentation}, // E0.6 [1] (🚏) bus stop
|
|
||||||
{0x1F690, 0x1F690, prEmojiPresentation}, // E1.0 [1] (🚐) minibus
|
|
||||||
{0x1F691, 0x1F693, prEmojiPresentation}, // E0.6 [3] (🚑..🚓) ambulance..police car
|
|
||||||
{0x1F694, 0x1F694, prEmojiPresentation}, // E0.7 [1] (🚔) oncoming police car
|
|
||||||
{0x1F695, 0x1F695, prEmojiPresentation}, // E0.6 [1] (🚕) taxi
|
|
||||||
{0x1F696, 0x1F696, prEmojiPresentation}, // E1.0 [1] (🚖) oncoming taxi
|
|
||||||
{0x1F697, 0x1F697, prEmojiPresentation}, // E0.6 [1] (🚗) automobile
|
|
||||||
{0x1F698, 0x1F698, prEmojiPresentation}, // E0.7 [1] (🚘) oncoming automobile
|
|
||||||
{0x1F699, 0x1F69A, prEmojiPresentation}, // E0.6 [2] (🚙..🚚) sport utility vehicle..delivery truck
|
|
||||||
{0x1F69B, 0x1F6A1, prEmojiPresentation}, // E1.0 [7] (🚛..🚡) articulated lorry..aerial tramway
|
|
||||||
{0x1F6A2, 0x1F6A2, prEmojiPresentation}, // E0.6 [1] (🚢) ship
|
|
||||||
{0x1F6A3, 0x1F6A3, prEmojiPresentation}, // E1.0 [1] (🚣) person rowing boat
|
|
||||||
{0x1F6A4, 0x1F6A5, prEmojiPresentation}, // E0.6 [2] (🚤..🚥) speedboat..horizontal traffic light
|
|
||||||
{0x1F6A6, 0x1F6A6, prEmojiPresentation}, // E1.0 [1] (🚦) vertical traffic light
|
|
||||||
{0x1F6A7, 0x1F6AD, prEmojiPresentation}, // E0.6 [7] (🚧..🚭) construction..no smoking
|
|
||||||
{0x1F6AE, 0x1F6B1, prEmojiPresentation}, // E1.0 [4] (🚮..🚱) litter in bin sign..non-potable water
|
|
||||||
{0x1F6B2, 0x1F6B2, prEmojiPresentation}, // E0.6 [1] (🚲) bicycle
|
|
||||||
{0x1F6B3, 0x1F6B5, prEmojiPresentation}, // E1.0 [3] (🚳..🚵) no bicycles..person mountain biking
|
|
||||||
{0x1F6B6, 0x1F6B6, prEmojiPresentation}, // E0.6 [1] (🚶) person walking
|
|
||||||
{0x1F6B7, 0x1F6B8, prEmojiPresentation}, // E1.0 [2] (🚷..🚸) no pedestrians..children crossing
|
|
||||||
{0x1F6B9, 0x1F6BE, prEmojiPresentation}, // E0.6 [6] (🚹..🚾) men’s room..water closet
|
|
||||||
{0x1F6BF, 0x1F6BF, prEmojiPresentation}, // E1.0 [1] (🚿) shower
|
|
||||||
{0x1F6C0, 0x1F6C0, prEmojiPresentation}, // E0.6 [1] (🛀) person taking bath
|
|
||||||
{0x1F6C1, 0x1F6C5, prEmojiPresentation}, // E1.0 [5] (🛁..🛅) bathtub..left luggage
|
|
||||||
{0x1F6CC, 0x1F6CC, prEmojiPresentation}, // E1.0 [1] (🛌) person in bed
|
|
||||||
{0x1F6D0, 0x1F6D0, prEmojiPresentation}, // E1.0 [1] (🛐) place of worship
|
|
||||||
{0x1F6D1, 0x1F6D2, prEmojiPresentation}, // E3.0 [2] (🛑..🛒) stop sign..shopping cart
|
|
||||||
{0x1F6D5, 0x1F6D5, prEmojiPresentation}, // E12.0 [1] (🛕) hindu temple
|
|
||||||
{0x1F6D6, 0x1F6D7, prEmojiPresentation}, // E13.0 [2] (🛖..🛗) hut..elevator
|
|
||||||
{0x1F6DC, 0x1F6DC, prEmojiPresentation}, // E15.0 [1] (🛜) wireless
|
|
||||||
{0x1F6DD, 0x1F6DF, prEmojiPresentation}, // E14.0 [3] (🛝..🛟) playground slide..ring buoy
|
|
||||||
{0x1F6EB, 0x1F6EC, prEmojiPresentation}, // E1.0 [2] (🛫..🛬) airplane departure..airplane arrival
|
|
||||||
{0x1F6F4, 0x1F6F6, prEmojiPresentation}, // E3.0 [3] (🛴..🛶) kick scooter..canoe
|
|
||||||
{0x1F6F7, 0x1F6F8, prEmojiPresentation}, // E5.0 [2] (🛷..🛸) sled..flying saucer
|
|
||||||
{0x1F6F9, 0x1F6F9, prEmojiPresentation}, // E11.0 [1] (🛹) skateboard
|
|
||||||
{0x1F6FA, 0x1F6FA, prEmojiPresentation}, // E12.0 [1] (🛺) auto rickshaw
|
|
||||||
{0x1F6FB, 0x1F6FC, prEmojiPresentation}, // E13.0 [2] (🛻..🛼) pickup truck..roller skate
|
|
||||||
{0x1F7E0, 0x1F7EB, prEmojiPresentation}, // E12.0 [12] (🟠..🟫) orange circle..brown square
|
|
||||||
{0x1F7F0, 0x1F7F0, prEmojiPresentation}, // E14.0 [1] (🟰) heavy equals sign
|
|
||||||
{0x1F90C, 0x1F90C, prEmojiPresentation}, // E13.0 [1] (🤌) pinched fingers
|
|
||||||
{0x1F90D, 0x1F90F, prEmojiPresentation}, // E12.0 [3] (🤍..🤏) white heart..pinching hand
|
|
||||||
{0x1F910, 0x1F918, prEmojiPresentation}, // E1.0 [9] (🤐..🤘) zipper-mouth face..sign of the horns
|
|
||||||
{0x1F919, 0x1F91E, prEmojiPresentation}, // E3.0 [6] (🤙..🤞) call me hand..crossed fingers
|
|
||||||
{0x1F91F, 0x1F91F, prEmojiPresentation}, // E5.0 [1] (🤟) love-you gesture
|
|
||||||
{0x1F920, 0x1F927, prEmojiPresentation}, // E3.0 [8] (🤠..🤧) cowboy hat face..sneezing face
|
|
||||||
{0x1F928, 0x1F92F, prEmojiPresentation}, // E5.0 [8] (🤨..🤯) face with raised eyebrow..exploding head
|
|
||||||
{0x1F930, 0x1F930, prEmojiPresentation}, // E3.0 [1] (🤰) pregnant woman
|
|
||||||
{0x1F931, 0x1F932, prEmojiPresentation}, // E5.0 [2] (🤱..🤲) breast-feeding..palms up together
|
|
||||||
{0x1F933, 0x1F93A, prEmojiPresentation}, // E3.0 [8] (🤳..🤺) selfie..person fencing
|
|
||||||
{0x1F93C, 0x1F93E, prEmojiPresentation}, // E3.0 [3] (🤼..🤾) people wrestling..person playing handball
|
|
||||||
{0x1F93F, 0x1F93F, prEmojiPresentation}, // E12.0 [1] (🤿) diving mask
|
|
||||||
{0x1F940, 0x1F945, prEmojiPresentation}, // E3.0 [6] (🥀..🥅) wilted flower..goal net
|
|
||||||
{0x1F947, 0x1F94B, prEmojiPresentation}, // E3.0 [5] (🥇..🥋) 1st place medal..martial arts uniform
|
|
||||||
{0x1F94C, 0x1F94C, prEmojiPresentation}, // E5.0 [1] (🥌) curling stone
|
|
||||||
{0x1F94D, 0x1F94F, prEmojiPresentation}, // E11.0 [3] (🥍..🥏) lacrosse..flying disc
|
|
||||||
{0x1F950, 0x1F95E, prEmojiPresentation}, // E3.0 [15] (🥐..🥞) croissant..pancakes
|
|
||||||
{0x1F95F, 0x1F96B, prEmojiPresentation}, // E5.0 [13] (🥟..🥫) dumpling..canned food
|
|
||||||
{0x1F96C, 0x1F970, prEmojiPresentation}, // E11.0 [5] (🥬..🥰) leafy green..smiling face with hearts
|
|
||||||
{0x1F971, 0x1F971, prEmojiPresentation}, // E12.0 [1] (🥱) yawning face
|
|
||||||
{0x1F972, 0x1F972, prEmojiPresentation}, // E13.0 [1] (🥲) smiling face with tear
|
|
||||||
{0x1F973, 0x1F976, prEmojiPresentation}, // E11.0 [4] (🥳..🥶) partying face..cold face
|
|
||||||
{0x1F977, 0x1F978, prEmojiPresentation}, // E13.0 [2] (🥷..🥸) ninja..disguised face
|
|
||||||
{0x1F979, 0x1F979, prEmojiPresentation}, // E14.0 [1] (🥹) face holding back tears
|
|
||||||
{0x1F97A, 0x1F97A, prEmojiPresentation}, // E11.0 [1] (🥺) pleading face
|
|
||||||
{0x1F97B, 0x1F97B, prEmojiPresentation}, // E12.0 [1] (🥻) sari
|
|
||||||
{0x1F97C, 0x1F97F, prEmojiPresentation}, // E11.0 [4] (🥼..🥿) lab coat..flat shoe
|
|
||||||
{0x1F980, 0x1F984, prEmojiPresentation}, // E1.0 [5] (🦀..🦄) crab..unicorn
|
|
||||||
{0x1F985, 0x1F991, prEmojiPresentation}, // E3.0 [13] (🦅..🦑) eagle..squid
|
|
||||||
{0x1F992, 0x1F997, prEmojiPresentation}, // E5.0 [6] (🦒..🦗) giraffe..cricket
|
|
||||||
{0x1F998, 0x1F9A2, prEmojiPresentation}, // E11.0 [11] (🦘..🦢) kangaroo..swan
|
|
||||||
{0x1F9A3, 0x1F9A4, prEmojiPresentation}, // E13.0 [2] (🦣..🦤) mammoth..dodo
|
|
||||||
{0x1F9A5, 0x1F9AA, prEmojiPresentation}, // E12.0 [6] (🦥..🦪) sloth..oyster
|
|
||||||
{0x1F9AB, 0x1F9AD, prEmojiPresentation}, // E13.0 [3] (🦫..🦭) beaver..seal
|
|
||||||
{0x1F9AE, 0x1F9AF, prEmojiPresentation}, // E12.0 [2] (🦮..🦯) guide dog..white cane
|
|
||||||
{0x1F9B0, 0x1F9B9, prEmojiPresentation}, // E11.0 [10] (🦰..🦹) red hair..supervillain
|
|
||||||
{0x1F9BA, 0x1F9BF, prEmojiPresentation}, // E12.0 [6] (🦺..🦿) safety vest..mechanical leg
|
|
||||||
{0x1F9C0, 0x1F9C0, prEmojiPresentation}, // E1.0 [1] (🧀) cheese wedge
|
|
||||||
{0x1F9C1, 0x1F9C2, prEmojiPresentation}, // E11.0 [2] (🧁..🧂) cupcake..salt
|
|
||||||
{0x1F9C3, 0x1F9CA, prEmojiPresentation}, // E12.0 [8] (🧃..🧊) beverage box..ice
|
|
||||||
{0x1F9CB, 0x1F9CB, prEmojiPresentation}, // E13.0 [1] (🧋) bubble tea
|
|
||||||
{0x1F9CC, 0x1F9CC, prEmojiPresentation}, // E14.0 [1] (🧌) troll
|
|
||||||
{0x1F9CD, 0x1F9CF, prEmojiPresentation}, // E12.0 [3] (🧍..🧏) person standing..deaf person
|
|
||||||
{0x1F9D0, 0x1F9E6, prEmojiPresentation}, // E5.0 [23] (🧐..🧦) face with monocle..socks
|
|
||||||
{0x1F9E7, 0x1F9FF, prEmojiPresentation}, // E11.0 [25] (🧧..🧿) red envelope..nazar amulet
|
|
||||||
{0x1FA70, 0x1FA73, prEmojiPresentation}, // E12.0 [4] (🩰..🩳) ballet shoes..shorts
|
|
||||||
{0x1FA74, 0x1FA74, prEmojiPresentation}, // E13.0 [1] (🩴) thong sandal
|
|
||||||
{0x1FA75, 0x1FA77, prEmojiPresentation}, // E15.0 [3] (🩵..🩷) light blue heart..pink heart
|
|
||||||
{0x1FA78, 0x1FA7A, prEmojiPresentation}, // E12.0 [3] (🩸..🩺) drop of blood..stethoscope
|
|
||||||
{0x1FA7B, 0x1FA7C, prEmojiPresentation}, // E14.0 [2] (🩻..🩼) x-ray..crutch
|
|
||||||
{0x1FA80, 0x1FA82, prEmojiPresentation}, // E12.0 [3] (🪀..🪂) yo-yo..parachute
|
|
||||||
{0x1FA83, 0x1FA86, prEmojiPresentation}, // E13.0 [4] (🪃..🪆) boomerang..nesting dolls
|
|
||||||
{0x1FA87, 0x1FA88, prEmojiPresentation}, // E15.0 [2] (🪇..🪈) maracas..flute
|
|
||||||
{0x1FA90, 0x1FA95, prEmojiPresentation}, // E12.0 [6] (🪐..🪕) ringed planet..banjo
|
|
||||||
{0x1FA96, 0x1FAA8, prEmojiPresentation}, // E13.0 [19] (🪖..🪨) military helmet..rock
|
|
||||||
{0x1FAA9, 0x1FAAC, prEmojiPresentation}, // E14.0 [4] (🪩..🪬) mirror ball..hamsa
|
|
||||||
{0x1FAAD, 0x1FAAF, prEmojiPresentation}, // E15.0 [3] (🪭..🪯) folding hand fan..khanda
|
|
||||||
{0x1FAB0, 0x1FAB6, prEmojiPresentation}, // E13.0 [7] (🪰..🪶) fly..feather
|
|
||||||
{0x1FAB7, 0x1FABA, prEmojiPresentation}, // E14.0 [4] (🪷..🪺) lotus..nest with eggs
|
|
||||||
{0x1FABB, 0x1FABD, prEmojiPresentation}, // E15.0 [3] (🪻..🪽) hyacinth..wing
|
|
||||||
{0x1FABF, 0x1FABF, prEmojiPresentation}, // E15.0 [1] (🪿) goose
|
|
||||||
{0x1FAC0, 0x1FAC2, prEmojiPresentation}, // E13.0 [3] (🫀..🫂) anatomical heart..people hugging
|
|
||||||
{0x1FAC3, 0x1FAC5, prEmojiPresentation}, // E14.0 [3] (🫃..🫅) pregnant man..person with crown
|
|
||||||
{0x1FACE, 0x1FACF, prEmojiPresentation}, // E15.0 [2] (🫎..🫏) moose..donkey
|
|
||||||
{0x1FAD0, 0x1FAD6, prEmojiPresentation}, // E13.0 [7] (🫐..🫖) blueberries..teapot
|
|
||||||
{0x1FAD7, 0x1FAD9, prEmojiPresentation}, // E14.0 [3] (🫗..🫙) pouring liquid..jar
|
|
||||||
{0x1FADA, 0x1FADB, prEmojiPresentation}, // E15.0 [2] (🫚..🫛) ginger root..pea pod
|
|
||||||
{0x1FAE0, 0x1FAE7, prEmojiPresentation}, // E14.0 [8] (🫠..🫧) melting face..bubbles
|
|
||||||
{0x1FAE8, 0x1FAE8, prEmojiPresentation}, // E15.0 [1] (🫨) shaking face
|
|
||||||
{0x1FAF0, 0x1FAF6, prEmojiPresentation}, // E14.0 [7] (🫰..🫶) hand with index finger and thumb crossed..heart hands
|
|
||||||
{0x1FAF7, 0x1FAF8, prEmojiPresentation}, // E15.0 [2] (🫷..🫸) leftwards pushing hand..rightwards pushing hand
|
|
||||||
}
|
|
||||||
215
vendor/github.com/rivo/uniseg/gen_breaktest.go
generated
vendored
215
vendor/github.com/rivo/uniseg/gen_breaktest.go
generated
vendored
|
|
@ -1,215 +0,0 @@
|
||||||
//go:build generate
|
|
||||||
|
|
||||||
// This program generates a Go containing a slice of test cases based on the
|
|
||||||
// Unicode Character Database auxiliary data files. The command line arguments
|
|
||||||
// are as follows:
|
|
||||||
//
|
|
||||||
// 1. The name of the Unicode data file (just the filename, without extension).
|
|
||||||
// 2. The name of the locally generated Go file.
|
|
||||||
// 3. The name of the slice containing the test cases.
|
|
||||||
// 4. The name of the generator, for logging purposes.
|
|
||||||
//
|
|
||||||
//go:generate go run gen_breaktest.go GraphemeBreakTest graphemebreak_test.go graphemeBreakTestCases graphemes
|
|
||||||
//go:generate go run gen_breaktest.go WordBreakTest wordbreak_test.go wordBreakTestCases words
|
|
||||||
//go:generate go run gen_breaktest.go SentenceBreakTest sentencebreak_test.go sentenceBreakTestCases sentences
|
|
||||||
//go:generate go run gen_breaktest.go LineBreakTest linebreak_test.go lineBreakTestCases lines
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"go/format"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// We want to test against a specific version rather than the latest. When the
|
|
||||||
// package is upgraded to a new version, change these to generate new tests.
|
|
||||||
const (
|
|
||||||
testCaseURL = `https://www.unicode.org/Public/15.0.0/ucd/auxiliary/%s.txt`
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
if len(os.Args) < 5 {
|
|
||||||
fmt.Println("Not enough arguments, see code for details")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.SetPrefix("gen_breaktest (" + os.Args[4] + "): ")
|
|
||||||
log.SetFlags(0)
|
|
||||||
|
|
||||||
// Read text of testcases and parse into Go source code.
|
|
||||||
src, err := parse(fmt.Sprintf(testCaseURL, os.Args[1]))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format the Go code.
|
|
||||||
formatted, err := format.Source(src)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln("gofmt:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write it out.
|
|
||||||
log.Print("Writing to ", os.Args[2])
|
|
||||||
if err := ioutil.WriteFile(os.Args[2], formatted, 0644); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse reads a break text file, either from a local file or from a URL. It
|
|
||||||
// parses the file data into Go source code representing the test cases.
|
|
||||||
func parse(url string) ([]byte, error) {
|
|
||||||
log.Printf("Parsing %s", url)
|
|
||||||
res, err := http.Get(url)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
body := res.Body
|
|
||||||
defer body.Close()
|
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
buf.Grow(120 << 10)
|
|
||||||
buf.WriteString(`// Code generated via go generate from gen_breaktest.go. DO NOT EDIT.
|
|
||||||
|
|
||||||
package uniseg
|
|
||||||
|
|
||||||
// ` + os.Args[3] + ` are Grapheme testcases taken from
|
|
||||||
// ` + url + `
|
|
||||||
// on ` + time.Now().Format("January 2, 2006") + `. See
|
|
||||||
// https://www.unicode.org/license.html for the Unicode license agreement.
|
|
||||||
var ` + os.Args[3] + ` = []testCase {
|
|
||||||
`)
|
|
||||||
|
|
||||||
sc := bufio.NewScanner(body)
|
|
||||||
num := 1
|
|
||||||
var line []byte
|
|
||||||
original := make([]byte, 0, 64)
|
|
||||||
expected := make([]byte, 0, 64)
|
|
||||||
for sc.Scan() {
|
|
||||||
num++
|
|
||||||
line = sc.Bytes()
|
|
||||||
if len(line) == 0 || line[0] == '#' {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var comment []byte
|
|
||||||
if i := bytes.IndexByte(line, '#'); i >= 0 {
|
|
||||||
comment = bytes.TrimSpace(line[i+1:])
|
|
||||||
line = bytes.TrimSpace(line[:i])
|
|
||||||
}
|
|
||||||
original, expected, err := parseRuneSequence(line, original[:0], expected[:0])
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf(`line %d: %v: %q`, num, err, line)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(buf, "\t{original: \"%s\", expected: %s}, // %s\n", original, expected, comment)
|
|
||||||
}
|
|
||||||
if err := sc.Err(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for final "# EOF", useful check if we're streaming via HTTP
|
|
||||||
if !bytes.Equal(line, []byte("# EOF")) {
|
|
||||||
return nil, fmt.Errorf(`line %d: exected "# EOF" as final line, got %q`, num, line)
|
|
||||||
}
|
|
||||||
buf.WriteString("}\n")
|
|
||||||
return buf.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used by parseRuneSequence to match input via bytes.HasPrefix.
|
|
||||||
var (
|
|
||||||
prefixBreak = []byte("÷ ")
|
|
||||||
prefixDontBreak = []byte("× ")
|
|
||||||
breakOk = []byte("÷")
|
|
||||||
breakNo = []byte("×")
|
|
||||||
)
|
|
||||||
|
|
||||||
// parseRuneSequence parses a rune + breaking opportunity sequence from b
|
|
||||||
// and appends the Go code for testcase.original to orig
|
|
||||||
// and appends the Go code for testcase.expected to exp.
|
|
||||||
// It retuns the new orig and exp slices.
|
|
||||||
//
|
|
||||||
// E.g. for the input b="÷ 0020 × 0308 ÷ 1F1E6 ÷"
|
|
||||||
// it will append
|
|
||||||
//
|
|
||||||
// "\u0020\u0308\U0001F1E6"
|
|
||||||
//
|
|
||||||
// and "[][]rune{{0x0020,0x0308},{0x1F1E6},}"
|
|
||||||
// to orig and exp respectively.
|
|
||||||
//
|
|
||||||
// The formatting of exp is expected to be cleaned up by gofmt or format.Source.
|
|
||||||
// Note we explicitly require the sequence to start with ÷ and we implicitly
|
|
||||||
// require it to end with ÷.
|
|
||||||
func parseRuneSequence(b, orig, exp []byte) ([]byte, []byte, error) {
|
|
||||||
// Check for and remove first ÷ or ×.
|
|
||||||
if !bytes.HasPrefix(b, prefixBreak) && !bytes.HasPrefix(b, prefixDontBreak) {
|
|
||||||
return nil, nil, errors.New("expected ÷ or × as first character")
|
|
||||||
}
|
|
||||||
if bytes.HasPrefix(b, prefixBreak) {
|
|
||||||
b = b[len(prefixBreak):]
|
|
||||||
} else {
|
|
||||||
b = b[len(prefixDontBreak):]
|
|
||||||
}
|
|
||||||
|
|
||||||
boundary := true
|
|
||||||
exp = append(exp, "[][]rune{"...)
|
|
||||||
for len(b) > 0 {
|
|
||||||
if boundary {
|
|
||||||
exp = append(exp, '{')
|
|
||||||
}
|
|
||||||
exp = append(exp, "0x"...)
|
|
||||||
// Find end of hex digits.
|
|
||||||
var i int
|
|
||||||
for i = 0; i < len(b) && b[i] != ' '; i++ {
|
|
||||||
if d := b[i]; ('0' <= d || d <= '9') ||
|
|
||||||
('A' <= d || d <= 'F') ||
|
|
||||||
('a' <= d || d <= 'f') {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return nil, nil, errors.New("bad hex digit")
|
|
||||||
}
|
|
||||||
switch i {
|
|
||||||
case 4:
|
|
||||||
orig = append(orig, "\\u"...)
|
|
||||||
case 5:
|
|
||||||
orig = append(orig, "\\U000"...)
|
|
||||||
default:
|
|
||||||
return nil, nil, errors.New("unsupport code point hex length")
|
|
||||||
}
|
|
||||||
orig = append(orig, b[:i]...)
|
|
||||||
exp = append(exp, b[:i]...)
|
|
||||||
b = b[i:]
|
|
||||||
|
|
||||||
// Check for space between hex and ÷ or ×.
|
|
||||||
if len(b) < 1 || b[0] != ' ' {
|
|
||||||
return nil, nil, errors.New("bad input")
|
|
||||||
}
|
|
||||||
b = b[1:]
|
|
||||||
|
|
||||||
// Check for next boundary.
|
|
||||||
switch {
|
|
||||||
case bytes.HasPrefix(b, breakOk):
|
|
||||||
boundary = true
|
|
||||||
b = b[len(breakOk):]
|
|
||||||
case bytes.HasPrefix(b, breakNo):
|
|
||||||
boundary = false
|
|
||||||
b = b[len(breakNo):]
|
|
||||||
default:
|
|
||||||
return nil, nil, errors.New("missing ÷ or ×")
|
|
||||||
}
|
|
||||||
if boundary {
|
|
||||||
exp = append(exp, '}')
|
|
||||||
}
|
|
||||||
exp = append(exp, ',')
|
|
||||||
if len(b) > 0 && b[0] == ' ' {
|
|
||||||
b = b[1:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exp = append(exp, '}')
|
|
||||||
return orig, exp, nil
|
|
||||||
}
|
|
||||||
261
vendor/github.com/rivo/uniseg/gen_properties.go
generated
vendored
261
vendor/github.com/rivo/uniseg/gen_properties.go
generated
vendored
|
|
@ -1,261 +0,0 @@
|
||||||
//go:build generate
|
|
||||||
|
|
||||||
// This program generates a property file in Go file from Unicode Character
|
|
||||||
// Database auxiliary data files. The command line arguments are as follows:
|
|
||||||
//
|
|
||||||
// 1. The name of the Unicode data file (just the filename, without extension).
|
|
||||||
// Can be "-" (to skip) if the emoji flag is included.
|
|
||||||
// 2. The name of the locally generated Go file.
|
|
||||||
// 3. The name of the slice mapping code points to properties.
|
|
||||||
// 4. The name of the generator, for logging purposes.
|
|
||||||
// 5. (Optional) Flags, comma-separated. The following flags are available:
|
|
||||||
// - "emojis=<property>": include the specified emoji properties (e.g.
|
|
||||||
// "Extended_Pictographic").
|
|
||||||
// - "gencat": include general category properties.
|
|
||||||
//
|
|
||||||
//go:generate go run gen_properties.go auxiliary/GraphemeBreakProperty graphemeproperties.go graphemeCodePoints graphemes emojis=Extended_Pictographic
|
|
||||||
//go:generate go run gen_properties.go auxiliary/WordBreakProperty wordproperties.go workBreakCodePoints words emojis=Extended_Pictographic
|
|
||||||
//go:generate go run gen_properties.go auxiliary/SentenceBreakProperty sentenceproperties.go sentenceBreakCodePoints sentences
|
|
||||||
//go:generate go run gen_properties.go LineBreak lineproperties.go lineBreakCodePoints lines gencat
|
|
||||||
//go:generate go run gen_properties.go EastAsianWidth eastasianwidth.go eastAsianWidth eastasianwidth
|
|
||||||
//go:generate go run gen_properties.go - emojipresentation.go emojiPresentation emojipresentation emojis=Emoji_Presentation
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"go/format"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"regexp"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// We want to test against a specific version rather than the latest. When the
|
|
||||||
// package is upgraded to a new version, change these to generate new tests.
|
|
||||||
const (
|
|
||||||
propertyURL = `https://www.unicode.org/Public/15.0.0/ucd/%s.txt`
|
|
||||||
emojiURL = `https://unicode.org/Public/15.0.0/ucd/emoji/emoji-data.txt`
|
|
||||||
)
|
|
||||||
|
|
||||||
// The regular expression for a line containing a code point range property.
|
|
||||||
var propertyPattern = regexp.MustCompile(`^([0-9A-F]{4,6})(\.\.([0-9A-F]{4,6}))?\s*;\s*([A-Za-z0-9_]+)\s*#\s(.+)$`)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
if len(os.Args) < 5 {
|
|
||||||
fmt.Println("Not enough arguments, see code for details")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.SetPrefix("gen_properties (" + os.Args[4] + "): ")
|
|
||||||
log.SetFlags(0)
|
|
||||||
|
|
||||||
// Parse flags.
|
|
||||||
flags := make(map[string]string)
|
|
||||||
if len(os.Args) >= 6 {
|
|
||||||
for _, flag := range strings.Split(os.Args[5], ",") {
|
|
||||||
flagFields := strings.Split(flag, "=")
|
|
||||||
if len(flagFields) == 1 {
|
|
||||||
flags[flagFields[0]] = "yes"
|
|
||||||
} else {
|
|
||||||
flags[flagFields[0]] = flagFields[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the text file and generate Go source code from it.
|
|
||||||
_, includeGeneralCategory := flags["gencat"]
|
|
||||||
var mainURL string
|
|
||||||
if os.Args[1] != "-" {
|
|
||||||
mainURL = fmt.Sprintf(propertyURL, os.Args[1])
|
|
||||||
}
|
|
||||||
src, err := parse(mainURL, flags["emojis"], includeGeneralCategory)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format the Go code.
|
|
||||||
formatted, err := format.Source([]byte(src))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("gofmt:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save it to the (local) target file.
|
|
||||||
log.Print("Writing to ", os.Args[2])
|
|
||||||
if err := ioutil.WriteFile(os.Args[2], formatted, 0644); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse parses the Unicode Properties text files located at the given URLs and
|
|
||||||
// returns their equivalent Go source code to be used in the uniseg package. If
|
|
||||||
// "emojiProperty" is not an empty string, emoji code points for that emoji
|
|
||||||
// property (e.g. "Extended_Pictographic") will be included. In those cases, you
|
|
||||||
// may pass an empty "propertyURL" to skip parsing the main properties file. If
|
|
||||||
// "includeGeneralCategory" is true, the Unicode General Category property will
|
|
||||||
// be extracted from the comments and included in the output.
|
|
||||||
func parse(propertyURL, emojiProperty string, includeGeneralCategory bool) (string, error) {
|
|
||||||
if propertyURL == "" && emojiProperty == "" {
|
|
||||||
return "", errors.New("no properties to parse")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Temporary buffer to hold properties.
|
|
||||||
var properties [][4]string
|
|
||||||
|
|
||||||
// Open the first URL.
|
|
||||||
if propertyURL != "" {
|
|
||||||
log.Printf("Parsing %s", propertyURL)
|
|
||||||
res, err := http.Get(propertyURL)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
in1 := res.Body
|
|
||||||
defer in1.Close()
|
|
||||||
|
|
||||||
// Parse it.
|
|
||||||
scanner := bufio.NewScanner(in1)
|
|
||||||
num := 0
|
|
||||||
for scanner.Scan() {
|
|
||||||
num++
|
|
||||||
line := strings.TrimSpace(scanner.Text())
|
|
||||||
|
|
||||||
// Skip comments and empty lines.
|
|
||||||
if strings.HasPrefix(line, "#") || line == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Everything else must be a code point range, a property and a comment.
|
|
||||||
from, to, property, comment, err := parseProperty(line)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("%s line %d: %v", os.Args[4], num, err)
|
|
||||||
}
|
|
||||||
properties = append(properties, [4]string{from, to, property, comment})
|
|
||||||
}
|
|
||||||
if err := scanner.Err(); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open the second URL.
|
|
||||||
if emojiProperty != "" {
|
|
||||||
log.Printf("Parsing %s", emojiURL)
|
|
||||||
res, err := http.Get(emojiURL)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
in2 := res.Body
|
|
||||||
defer in2.Close()
|
|
||||||
|
|
||||||
// Parse it.
|
|
||||||
scanner := bufio.NewScanner(in2)
|
|
||||||
num := 0
|
|
||||||
for scanner.Scan() {
|
|
||||||
num++
|
|
||||||
line := scanner.Text()
|
|
||||||
|
|
||||||
// Skip comments, empty lines, and everything not containing
|
|
||||||
// "Extended_Pictographic".
|
|
||||||
if strings.HasPrefix(line, "#") || line == "" || !strings.Contains(line, emojiProperty) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Everything else must be a code point range, a property and a comment.
|
|
||||||
from, to, property, comment, err := parseProperty(line)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("emojis line %d: %v", num, err)
|
|
||||||
}
|
|
||||||
properties = append(properties, [4]string{from, to, property, comment})
|
|
||||||
}
|
|
||||||
if err := scanner.Err(); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Avoid overflow during binary search.
|
|
||||||
if len(properties) >= 1<<31 {
|
|
||||||
return "", errors.New("too many properties")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort properties.
|
|
||||||
sort.Slice(properties, func(i, j int) bool {
|
|
||||||
left, _ := strconv.ParseUint(properties[i][0], 16, 64)
|
|
||||||
right, _ := strconv.ParseUint(properties[j][0], 16, 64)
|
|
||||||
return left < right
|
|
||||||
})
|
|
||||||
|
|
||||||
// Header.
|
|
||||||
var (
|
|
||||||
buf bytes.Buffer
|
|
||||||
emojiComment string
|
|
||||||
)
|
|
||||||
columns := 3
|
|
||||||
if includeGeneralCategory {
|
|
||||||
columns = 4
|
|
||||||
}
|
|
||||||
if emojiURL != "" {
|
|
||||||
emojiComment = `
|
|
||||||
// and
|
|
||||||
// ` + emojiURL + `
|
|
||||||
// ("Extended_Pictographic" only)`
|
|
||||||
}
|
|
||||||
buf.WriteString(`// Code generated via go generate from gen_properties.go. DO NOT EDIT.
|
|
||||||
|
|
||||||
package uniseg
|
|
||||||
|
|
||||||
// ` + os.Args[3] + ` are taken from
|
|
||||||
// ` + propertyURL + emojiComment + `
|
|
||||||
// on ` + time.Now().Format("January 2, 2006") + `. See https://www.unicode.org/license.html for the Unicode
|
|
||||||
// license agreement.
|
|
||||||
var ` + os.Args[3] + ` = [][` + strconv.Itoa(columns) + `]int{
|
|
||||||
`)
|
|
||||||
|
|
||||||
// Properties.
|
|
||||||
for _, prop := range properties {
|
|
||||||
if includeGeneralCategory {
|
|
||||||
generalCategory := "gc" + prop[3][:2]
|
|
||||||
if generalCategory == "gcL&" {
|
|
||||||
generalCategory = "gcLC"
|
|
||||||
}
|
|
||||||
prop[3] = prop[3][3:]
|
|
||||||
fmt.Fprintf(&buf, "{0x%s,0x%s,%s,%s}, // %s\n", prop[0], prop[1], translateProperty("pr", prop[2]), generalCategory, prop[3])
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(&buf, "{0x%s,0x%s,%s}, // %s\n", prop[0], prop[1], translateProperty("pr", prop[2]), prop[3])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tail.
|
|
||||||
buf.WriteString("}")
|
|
||||||
|
|
||||||
return buf.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseProperty parses a line of the Unicode properties text file containing a
|
|
||||||
// property for a code point range and returns it along with its comment.
|
|
||||||
func parseProperty(line string) (from, to, property, comment string, err error) {
|
|
||||||
fields := propertyPattern.FindStringSubmatch(line)
|
|
||||||
if fields == nil {
|
|
||||||
err = errors.New("no property found")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
from = fields[1]
|
|
||||||
to = fields[3]
|
|
||||||
if to == "" {
|
|
||||||
to = from
|
|
||||||
}
|
|
||||||
property = fields[4]
|
|
||||||
comment = fields[5]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// translateProperty translates a property name as used in the Unicode data file
|
|
||||||
// to a variable used in the Go code.
|
|
||||||
func translateProperty(prefix, property string) string {
|
|
||||||
return prefix + strings.ReplaceAll(property, "_", "")
|
|
||||||
}
|
|
||||||
331
vendor/github.com/rivo/uniseg/grapheme.go
generated
vendored
331
vendor/github.com/rivo/uniseg/grapheme.go
generated
vendored
|
|
@ -1,331 +0,0 @@
|
||||||
package uniseg
|
|
||||||
|
|
||||||
import "unicode/utf8"
|
|
||||||
|
|
||||||
// Graphemes implements an iterator over Unicode grapheme clusters, or
|
|
||||||
// user-perceived characters. While iterating, it also provides information
|
|
||||||
// about word boundaries, sentence boundaries, line breaks, and monospace
|
|
||||||
// character widths.
|
|
||||||
//
|
|
||||||
// After constructing the class via [NewGraphemes] for a given string "str",
|
|
||||||
// [Graphemes.Next] is called for every grapheme cluster in a loop until it
|
|
||||||
// returns false. Inside the loop, information about the grapheme cluster as
|
|
||||||
// well as boundary information and character width is available via the various
|
|
||||||
// methods (see examples below).
|
|
||||||
//
|
|
||||||
// This class basically wraps the [StepString] parser and provides a convenient
|
|
||||||
// interface to it. If you are only interested in some parts of this package's
|
|
||||||
// functionality, using the specialized functions starting with "First" is
|
|
||||||
// almost always faster.
|
|
||||||
type Graphemes struct {
|
|
||||||
// The original string.
|
|
||||||
original string
|
|
||||||
|
|
||||||
// The remaining string to be parsed.
|
|
||||||
remaining string
|
|
||||||
|
|
||||||
// The current grapheme cluster.
|
|
||||||
cluster string
|
|
||||||
|
|
||||||
// The byte offset of the current grapheme cluster relative to the original
|
|
||||||
// string.
|
|
||||||
offset int
|
|
||||||
|
|
||||||
// The current boundary information of the [Step] parser.
|
|
||||||
boundaries int
|
|
||||||
|
|
||||||
// The current state of the [Step] parser.
|
|
||||||
state int
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGraphemes returns a new grapheme cluster iterator.
|
|
||||||
func NewGraphemes(str string) *Graphemes {
|
|
||||||
return &Graphemes{
|
|
||||||
original: str,
|
|
||||||
remaining: str,
|
|
||||||
state: -1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next advances the iterator by one grapheme cluster and returns false if no
|
|
||||||
// clusters are left. This function must be called before the first cluster is
|
|
||||||
// accessed.
|
|
||||||
func (g *Graphemes) Next() bool {
|
|
||||||
if len(g.remaining) == 0 {
|
|
||||||
// We're already past the end.
|
|
||||||
g.state = -2
|
|
||||||
g.cluster = ""
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
g.offset += len(g.cluster)
|
|
||||||
g.cluster, g.remaining, g.boundaries, g.state = StepString(g.remaining, g.state)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Runes returns a slice of runes (code points) which corresponds to the current
|
|
||||||
// grapheme cluster. If the iterator is already past the end or [Graphemes.Next]
|
|
||||||
// has not yet been called, nil is returned.
|
|
||||||
func (g *Graphemes) Runes() []rune {
|
|
||||||
if g.state < 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return []rune(g.cluster)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Str returns a substring of the original string which corresponds to the
|
|
||||||
// current grapheme cluster. If the iterator is already past the end or
|
|
||||||
// [Graphemes.Next] has not yet been called, an empty string is returned.
|
|
||||||
func (g *Graphemes) Str() string {
|
|
||||||
return g.cluster
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bytes returns a byte slice which corresponds to the current grapheme cluster.
|
|
||||||
// If the iterator is already past the end or [Graphemes.Next] has not yet been
|
|
||||||
// called, nil is returned.
|
|
||||||
func (g *Graphemes) Bytes() []byte {
|
|
||||||
if g.state < 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return []byte(g.cluster)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Positions returns the interval of the current grapheme cluster as byte
|
|
||||||
// positions into the original string. The first returned value "from" indexes
|
|
||||||
// the first byte and the second returned value "to" indexes the first byte that
|
|
||||||
// is not included anymore, i.e. str[from:to] is the current grapheme cluster of
|
|
||||||
// the original string "str". If [Graphemes.Next] has not yet been called, both
|
|
||||||
// values are 0. If the iterator is already past the end, both values are 1.
|
|
||||||
func (g *Graphemes) Positions() (int, int) {
|
|
||||||
if g.state == -1 {
|
|
||||||
return 0, 0
|
|
||||||
} else if g.state == -2 {
|
|
||||||
return 1, 1
|
|
||||||
}
|
|
||||||
return g.offset, g.offset + len(g.cluster)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsWordBoundary returns true if a word ends after the current grapheme
|
|
||||||
// cluster.
|
|
||||||
func (g *Graphemes) IsWordBoundary() bool {
|
|
||||||
if g.state < 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return g.boundaries&MaskWord != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsSentenceBoundary returns true if a sentence ends after the current
|
|
||||||
// grapheme cluster.
|
|
||||||
func (g *Graphemes) IsSentenceBoundary() bool {
|
|
||||||
if g.state < 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return g.boundaries&MaskSentence != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// LineBreak returns whether the line can be broken after the current grapheme
|
|
||||||
// cluster. A value of [LineDontBreak] means the line may not be broken, a value
|
|
||||||
// of [LineMustBreak] means the line must be broken, and a value of
|
|
||||||
// [LineCanBreak] means the line may or may not be broken.
|
|
||||||
func (g *Graphemes) LineBreak() int {
|
|
||||||
if g.state == -1 {
|
|
||||||
return LineDontBreak
|
|
||||||
}
|
|
||||||
if g.state == -2 {
|
|
||||||
return LineMustBreak
|
|
||||||
}
|
|
||||||
return g.boundaries & MaskLine
|
|
||||||
}
|
|
||||||
|
|
||||||
// Width returns the monospace width of the current grapheme cluster.
|
|
||||||
func (g *Graphemes) Width() int {
|
|
||||||
if g.state < 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return g.boundaries >> ShiftWidth
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset puts the iterator into its initial state such that the next call to
|
|
||||||
// [Graphemes.Next] sets it to the first grapheme cluster again.
|
|
||||||
func (g *Graphemes) Reset() {
|
|
||||||
g.state = -1
|
|
||||||
g.offset = 0
|
|
||||||
g.cluster = ""
|
|
||||||
g.remaining = g.original
|
|
||||||
}
|
|
||||||
|
|
||||||
// GraphemeClusterCount returns the number of user-perceived characters
|
|
||||||
// (grapheme clusters) for the given string.
|
|
||||||
func GraphemeClusterCount(s string) (n int) {
|
|
||||||
state := -1
|
|
||||||
for len(s) > 0 {
|
|
||||||
_, s, _, state = FirstGraphemeClusterInString(s, state)
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReverseString reverses the given string while observing grapheme cluster
|
|
||||||
// boundaries.
|
|
||||||
func ReverseString(s string) string {
|
|
||||||
str := []byte(s)
|
|
||||||
reversed := make([]byte, len(str))
|
|
||||||
state := -1
|
|
||||||
index := len(str)
|
|
||||||
for len(str) > 0 {
|
|
||||||
var cluster []byte
|
|
||||||
cluster, str, _, state = FirstGraphemeCluster(str, state)
|
|
||||||
index -= len(cluster)
|
|
||||||
copy(reversed[index:], cluster)
|
|
||||||
if index <= len(str)/2 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return string(reversed)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The number of bits the grapheme property must be shifted to make place for
|
|
||||||
// grapheme states.
|
|
||||||
const shiftGraphemePropState = 4
|
|
||||||
|
|
||||||
// FirstGraphemeCluster returns the first grapheme cluster found in the given
|
|
||||||
// byte slice according to the rules of [Unicode Standard Annex #29, Grapheme
|
|
||||||
// Cluster Boundaries]. This function can be called continuously to extract all
|
|
||||||
// grapheme clusters from a byte slice, as illustrated in the example below.
|
|
||||||
//
|
|
||||||
// If you don't know the current state, for example when calling the function
|
|
||||||
// for the first time, you must pass -1. For consecutive calls, pass the state
|
|
||||||
// and rest slice returned by the previous call.
|
|
||||||
//
|
|
||||||
// The "rest" slice is the sub-slice of the original byte slice "b" starting
|
|
||||||
// after the last byte of the identified grapheme cluster. If the length of the
|
|
||||||
// "rest" slice is 0, the entire byte slice "b" has been processed. The
|
|
||||||
// "cluster" byte slice is the sub-slice of the input slice containing the
|
|
||||||
// identified grapheme cluster.
|
|
||||||
//
|
|
||||||
// The returned width is the width of the grapheme cluster for most monospace
|
|
||||||
// fonts where a value of 1 represents one character cell.
|
|
||||||
//
|
|
||||||
// Given an empty byte slice "b", the function returns nil values.
|
|
||||||
//
|
|
||||||
// While slightly less convenient than using the Graphemes class, this function
|
|
||||||
// has much better performance and makes no allocations. It lends itself well to
|
|
||||||
// large byte slices.
|
|
||||||
//
|
|
||||||
// [Unicode Standard Annex #29, Grapheme Cluster Boundaries]: http://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries
|
|
||||||
func FirstGraphemeCluster(b []byte, state int) (cluster, rest []byte, width, newState int) {
|
|
||||||
// An empty byte slice returns nothing.
|
|
||||||
if len(b) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the first rune.
|
|
||||||
r, length := utf8.DecodeRune(b)
|
|
||||||
if len(b) <= length { // If we're already past the end, there is nothing else to parse.
|
|
||||||
var prop int
|
|
||||||
if state < 0 {
|
|
||||||
prop = propertyGraphemes(r)
|
|
||||||
} else {
|
|
||||||
prop = state >> shiftGraphemePropState
|
|
||||||
}
|
|
||||||
return b, nil, runeWidth(r, prop), grAny | (prop << shiftGraphemePropState)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we don't know the state, determine it now.
|
|
||||||
var firstProp int
|
|
||||||
if state < 0 {
|
|
||||||
state, firstProp, _ = transitionGraphemeState(state, r)
|
|
||||||
} else {
|
|
||||||
firstProp = state >> shiftGraphemePropState
|
|
||||||
}
|
|
||||||
width += runeWidth(r, firstProp)
|
|
||||||
|
|
||||||
// Transition until we find a boundary.
|
|
||||||
for {
|
|
||||||
var (
|
|
||||||
prop int
|
|
||||||
boundary bool
|
|
||||||
)
|
|
||||||
|
|
||||||
r, l := utf8.DecodeRune(b[length:])
|
|
||||||
state, prop, boundary = transitionGraphemeState(state&maskGraphemeState, r)
|
|
||||||
|
|
||||||
if boundary {
|
|
||||||
return b[:length], b[length:], width, state | (prop << shiftGraphemePropState)
|
|
||||||
}
|
|
||||||
|
|
||||||
if firstProp == prExtendedPictographic {
|
|
||||||
if r == vs15 {
|
|
||||||
width = 1
|
|
||||||
} else if r == vs16 {
|
|
||||||
width = 2
|
|
||||||
}
|
|
||||||
} else if firstProp != prRegionalIndicator && firstProp != prL {
|
|
||||||
width += runeWidth(r, prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
length += l
|
|
||||||
if len(b) <= length {
|
|
||||||
return b, nil, width, grAny | (prop << shiftGraphemePropState)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FirstGraphemeClusterInString is like [FirstGraphemeCluster] but its input and
|
|
||||||
// outputs are strings.
|
|
||||||
func FirstGraphemeClusterInString(str string, state int) (cluster, rest string, width, newState int) {
|
|
||||||
// An empty string returns nothing.
|
|
||||||
if len(str) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the first rune.
|
|
||||||
r, length := utf8.DecodeRuneInString(str)
|
|
||||||
if len(str) <= length { // If we're already past the end, there is nothing else to parse.
|
|
||||||
var prop int
|
|
||||||
if state < 0 {
|
|
||||||
prop = propertyGraphemes(r)
|
|
||||||
} else {
|
|
||||||
prop = state >> shiftGraphemePropState
|
|
||||||
}
|
|
||||||
return str, "", runeWidth(r, prop), grAny | (prop << shiftGraphemePropState)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we don't know the state, determine it now.
|
|
||||||
var firstProp int
|
|
||||||
if state < 0 {
|
|
||||||
state, firstProp, _ = transitionGraphemeState(state, r)
|
|
||||||
} else {
|
|
||||||
firstProp = state >> shiftGraphemePropState
|
|
||||||
}
|
|
||||||
width += runeWidth(r, firstProp)
|
|
||||||
|
|
||||||
// Transition until we find a boundary.
|
|
||||||
for {
|
|
||||||
var (
|
|
||||||
prop int
|
|
||||||
boundary bool
|
|
||||||
)
|
|
||||||
|
|
||||||
r, l := utf8.DecodeRuneInString(str[length:])
|
|
||||||
state, prop, boundary = transitionGraphemeState(state&maskGraphemeState, r)
|
|
||||||
|
|
||||||
if boundary {
|
|
||||||
return str[:length], str[length:], width, state | (prop << shiftGraphemePropState)
|
|
||||||
}
|
|
||||||
|
|
||||||
if firstProp == prExtendedPictographic {
|
|
||||||
if r == vs15 {
|
|
||||||
width = 1
|
|
||||||
} else if r == vs16 {
|
|
||||||
width = 2
|
|
||||||
}
|
|
||||||
} else if firstProp != prRegionalIndicator && firstProp != prL {
|
|
||||||
width += runeWidth(r, prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
length += l
|
|
||||||
if len(str) <= length {
|
|
||||||
return str, "", width, grAny | (prop << shiftGraphemePropState)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1915
vendor/github.com/rivo/uniseg/graphemeproperties.go
generated
vendored
1915
vendor/github.com/rivo/uniseg/graphemeproperties.go
generated
vendored
File diff suppressed because it is too large
Load diff
176
vendor/github.com/rivo/uniseg/graphemerules.go
generated
vendored
176
vendor/github.com/rivo/uniseg/graphemerules.go
generated
vendored
|
|
@ -1,176 +0,0 @@
|
||||||
package uniseg
|
|
||||||
|
|
||||||
// The states of the grapheme cluster parser.
|
|
||||||
const (
|
|
||||||
grAny = iota
|
|
||||||
grCR
|
|
||||||
grControlLF
|
|
||||||
grL
|
|
||||||
grLVV
|
|
||||||
grLVTT
|
|
||||||
grPrepend
|
|
||||||
grExtendedPictographic
|
|
||||||
grExtendedPictographicZWJ
|
|
||||||
grRIOdd
|
|
||||||
grRIEven
|
|
||||||
)
|
|
||||||
|
|
||||||
// The grapheme cluster parser's breaking instructions.
|
|
||||||
const (
|
|
||||||
grNoBoundary = iota
|
|
||||||
grBoundary
|
|
||||||
)
|
|
||||||
|
|
||||||
// grTransitions implements the grapheme cluster parser's state transitions.
|
|
||||||
// Maps state and property to a new state, a breaking instruction, and rule
|
|
||||||
// number. The breaking instruction always refers to the boundary between the
|
|
||||||
// last and next code point. Returns negative values if no transition is found.
|
|
||||||
//
|
|
||||||
// This function is used as follows:
|
|
||||||
//
|
|
||||||
// 1. Find specific state + specific property. Stop if found.
|
|
||||||
// 2. Find specific state + any property.
|
|
||||||
// 3. Find any state + specific property.
|
|
||||||
// 4. If only (2) or (3) (but not both) was found, stop.
|
|
||||||
// 5. If both (2) and (3) were found, use state from (3) and breaking instruction
|
|
||||||
// from the transition with the lower rule number, prefer (3) if rule numbers
|
|
||||||
// are equal. Stop.
|
|
||||||
// 6. Assume grAny and grBoundary.
|
|
||||||
//
|
|
||||||
// Unicode version 15.0.0.
|
|
||||||
func grTransitions(state, prop int) (newState int, newProp int, boundary int) {
|
|
||||||
// It turns out that using a big switch statement is much faster than using
|
|
||||||
// a map.
|
|
||||||
|
|
||||||
switch uint64(state) | uint64(prop)<<32 {
|
|
||||||
// GB5
|
|
||||||
case grAny | prCR<<32:
|
|
||||||
return grCR, grBoundary, 50
|
|
||||||
case grAny | prLF<<32:
|
|
||||||
return grControlLF, grBoundary, 50
|
|
||||||
case grAny | prControl<<32:
|
|
||||||
return grControlLF, grBoundary, 50
|
|
||||||
|
|
||||||
// GB4
|
|
||||||
case grCR | prAny<<32:
|
|
||||||
return grAny, grBoundary, 40
|
|
||||||
case grControlLF | prAny<<32:
|
|
||||||
return grAny, grBoundary, 40
|
|
||||||
|
|
||||||
// GB3
|
|
||||||
case grCR | prLF<<32:
|
|
||||||
return grControlLF, grNoBoundary, 30
|
|
||||||
|
|
||||||
// GB6
|
|
||||||
case grAny | prL<<32:
|
|
||||||
return grL, grBoundary, 9990
|
|
||||||
case grL | prL<<32:
|
|
||||||
return grL, grNoBoundary, 60
|
|
||||||
case grL | prV<<32:
|
|
||||||
return grLVV, grNoBoundary, 60
|
|
||||||
case grL | prLV<<32:
|
|
||||||
return grLVV, grNoBoundary, 60
|
|
||||||
case grL | prLVT<<32:
|
|
||||||
return grLVTT, grNoBoundary, 60
|
|
||||||
|
|
||||||
// GB7
|
|
||||||
case grAny | prLV<<32:
|
|
||||||
return grLVV, grBoundary, 9990
|
|
||||||
case grAny | prV<<32:
|
|
||||||
return grLVV, grBoundary, 9990
|
|
||||||
case grLVV | prV<<32:
|
|
||||||
return grLVV, grNoBoundary, 70
|
|
||||||
case grLVV | prT<<32:
|
|
||||||
return grLVTT, grNoBoundary, 70
|
|
||||||
|
|
||||||
// GB8
|
|
||||||
case grAny | prLVT<<32:
|
|
||||||
return grLVTT, grBoundary, 9990
|
|
||||||
case grAny | prT<<32:
|
|
||||||
return grLVTT, grBoundary, 9990
|
|
||||||
case grLVTT | prT<<32:
|
|
||||||
return grLVTT, grNoBoundary, 80
|
|
||||||
|
|
||||||
// GB9
|
|
||||||
case grAny | prExtend<<32:
|
|
||||||
return grAny, grNoBoundary, 90
|
|
||||||
case grAny | prZWJ<<32:
|
|
||||||
return grAny, grNoBoundary, 90
|
|
||||||
|
|
||||||
// GB9a
|
|
||||||
case grAny | prSpacingMark<<32:
|
|
||||||
return grAny, grNoBoundary, 91
|
|
||||||
|
|
||||||
// GB9b
|
|
||||||
case grAny | prPrepend<<32:
|
|
||||||
return grPrepend, grBoundary, 9990
|
|
||||||
case grPrepend | prAny<<32:
|
|
||||||
return grAny, grNoBoundary, 92
|
|
||||||
|
|
||||||
// GB11
|
|
||||||
case grAny | prExtendedPictographic<<32:
|
|
||||||
return grExtendedPictographic, grBoundary, 9990
|
|
||||||
case grExtendedPictographic | prExtend<<32:
|
|
||||||
return grExtendedPictographic, grNoBoundary, 110
|
|
||||||
case grExtendedPictographic | prZWJ<<32:
|
|
||||||
return grExtendedPictographicZWJ, grNoBoundary, 110
|
|
||||||
case grExtendedPictographicZWJ | prExtendedPictographic<<32:
|
|
||||||
return grExtendedPictographic, grNoBoundary, 110
|
|
||||||
|
|
||||||
// GB12 / GB13
|
|
||||||
case grAny | prRegionalIndicator<<32:
|
|
||||||
return grRIOdd, grBoundary, 9990
|
|
||||||
case grRIOdd | prRegionalIndicator<<32:
|
|
||||||
return grRIEven, grNoBoundary, 120
|
|
||||||
case grRIEven | prRegionalIndicator<<32:
|
|
||||||
return grRIOdd, grBoundary, 120
|
|
||||||
default:
|
|
||||||
return -1, -1, -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// transitionGraphemeState determines the new state of the grapheme cluster
|
|
||||||
// parser given the current state and the next code point. It also returns the
|
|
||||||
// code point's grapheme property (the value mapped by the [graphemeCodePoints]
|
|
||||||
// table) and whether a cluster boundary was detected.
|
|
||||||
func transitionGraphemeState(state int, r rune) (newState, prop int, boundary bool) {
|
|
||||||
// Determine the property of the next character.
|
|
||||||
prop = propertyGraphemes(r)
|
|
||||||
|
|
||||||
// Find the applicable transition.
|
|
||||||
nextState, nextProp, _ := grTransitions(state, prop)
|
|
||||||
if nextState >= 0 {
|
|
||||||
// We have a specific transition. We'll use it.
|
|
||||||
return nextState, prop, nextProp == grBoundary
|
|
||||||
}
|
|
||||||
|
|
||||||
// No specific transition found. Try the less specific ones.
|
|
||||||
anyPropState, anyPropProp, anyPropRule := grTransitions(state, prAny)
|
|
||||||
anyStateState, anyStateProp, anyStateRule := grTransitions(grAny, prop)
|
|
||||||
if anyPropState >= 0 && anyStateState >= 0 {
|
|
||||||
// Both apply. We'll use a mix (see comments for grTransitions).
|
|
||||||
newState = anyStateState
|
|
||||||
boundary = anyStateProp == grBoundary
|
|
||||||
if anyPropRule < anyStateRule {
|
|
||||||
boundary = anyPropProp == grBoundary
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if anyPropState >= 0 {
|
|
||||||
// We only have a specific state.
|
|
||||||
return anyPropState, prop, anyPropProp == grBoundary
|
|
||||||
// This branch will probably never be reached because okAnyState will
|
|
||||||
// always be true given the current transition map. But we keep it here
|
|
||||||
// for future modifications to the transition map where this may not be
|
|
||||||
// true anymore.
|
|
||||||
}
|
|
||||||
|
|
||||||
if anyStateState >= 0 {
|
|
||||||
// We only have a specific property.
|
|
||||||
return anyStateState, prop, anyStateProp == grBoundary
|
|
||||||
}
|
|
||||||
|
|
||||||
// No known transition. GB999: Any ÷ Any.
|
|
||||||
return grAny, prop, true
|
|
||||||
}
|
|
||||||
134
vendor/github.com/rivo/uniseg/line.go
generated
vendored
134
vendor/github.com/rivo/uniseg/line.go
generated
vendored
|
|
@ -1,134 +0,0 @@
|
||||||
package uniseg
|
|
||||||
|
|
||||||
import "unicode/utf8"
|
|
||||||
|
|
||||||
// FirstLineSegment returns the prefix of the given byte slice after which a
|
|
||||||
// decision to break the string over to the next line can or must be made,
|
|
||||||
// according to the rules of [Unicode Standard Annex #14]. This is used to
|
|
||||||
// implement line breaking.
|
|
||||||
//
|
|
||||||
// Line breaking, also known as word wrapping, is the process of breaking a
|
|
||||||
// section of text into lines such that it will fit in the available width of a
|
|
||||||
// page, window or other display area.
|
|
||||||
//
|
|
||||||
// The returned "segment" may not be broken into smaller parts, unless no other
|
|
||||||
// breaking opportunities present themselves, in which case you may break by
|
|
||||||
// grapheme clusters (using the [FirstGraphemeCluster] function to determine the
|
|
||||||
// grapheme clusters).
|
|
||||||
//
|
|
||||||
// The "mustBreak" flag indicates whether you MUST break the line after the
|
|
||||||
// given segment (true), for example after newline characters, or you MAY break
|
|
||||||
// the line after the given segment (false).
|
|
||||||
//
|
|
||||||
// This function can be called continuously to extract all non-breaking sub-sets
|
|
||||||
// from a byte slice, as illustrated in the example below.
|
|
||||||
//
|
|
||||||
// If you don't know the current state, for example when calling the function
|
|
||||||
// for the first time, you must pass -1. For consecutive calls, pass the state
|
|
||||||
// and rest slice returned by the previous call.
|
|
||||||
//
|
|
||||||
// The "rest" slice is the sub-slice of the original byte slice "b" starting
|
|
||||||
// after the last byte of the identified line segment. If the length of the
|
|
||||||
// "rest" slice is 0, the entire byte slice "b" has been processed. The
|
|
||||||
// "segment" byte slice is the sub-slice of the input slice containing the
|
|
||||||
// identified line segment.
|
|
||||||
//
|
|
||||||
// Given an empty byte slice "b", the function returns nil values.
|
|
||||||
//
|
|
||||||
// Note that in accordance with [UAX #14 LB3], the final segment will end with
|
|
||||||
// "mustBreak" set to true. You can choose to ignore this by checking if the
|
|
||||||
// length of the "rest" slice is 0 and calling [HasTrailingLineBreak] or
|
|
||||||
// [HasTrailingLineBreakInString] on the last rune.
|
|
||||||
//
|
|
||||||
// Note also that this algorithm may break within grapheme clusters. This is
|
|
||||||
// addressed in Section 8.2 Example 6 of UAX #14. To avoid this, you can use
|
|
||||||
// the [Step] function instead.
|
|
||||||
//
|
|
||||||
// [Unicode Standard Annex #14]: https://www.unicode.org/reports/tr14/
|
|
||||||
// [UAX #14 LB3]: https://www.unicode.org/reports/tr14/#Algorithm
|
|
||||||
func FirstLineSegment(b []byte, state int) (segment, rest []byte, mustBreak bool, newState int) {
|
|
||||||
// An empty byte slice returns nothing.
|
|
||||||
if len(b) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the first rune.
|
|
||||||
r, length := utf8.DecodeRune(b)
|
|
||||||
if len(b) <= length { // If we're already past the end, there is nothing else to parse.
|
|
||||||
return b, nil, true, lbAny // LB3.
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we don't know the state, determine it now.
|
|
||||||
if state < 0 {
|
|
||||||
state, _ = transitionLineBreakState(state, r, b[length:], "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transition until we find a boundary.
|
|
||||||
var boundary int
|
|
||||||
for {
|
|
||||||
r, l := utf8.DecodeRune(b[length:])
|
|
||||||
state, boundary = transitionLineBreakState(state, r, b[length+l:], "")
|
|
||||||
|
|
||||||
if boundary != LineDontBreak {
|
|
||||||
return b[:length], b[length:], boundary == LineMustBreak, state
|
|
||||||
}
|
|
||||||
|
|
||||||
length += l
|
|
||||||
if len(b) <= length {
|
|
||||||
return b, nil, true, lbAny // LB3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FirstLineSegmentInString is like [FirstLineSegment] but its input and outputs
|
|
||||||
// are strings.
|
|
||||||
func FirstLineSegmentInString(str string, state int) (segment, rest string, mustBreak bool, newState int) {
|
|
||||||
// An empty byte slice returns nothing.
|
|
||||||
if len(str) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the first rune.
|
|
||||||
r, length := utf8.DecodeRuneInString(str)
|
|
||||||
if len(str) <= length { // If we're already past the end, there is nothing else to parse.
|
|
||||||
return str, "", true, lbAny // LB3.
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we don't know the state, determine it now.
|
|
||||||
if state < 0 {
|
|
||||||
state, _ = transitionLineBreakState(state, r, nil, str[length:])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transition until we find a boundary.
|
|
||||||
var boundary int
|
|
||||||
for {
|
|
||||||
r, l := utf8.DecodeRuneInString(str[length:])
|
|
||||||
state, boundary = transitionLineBreakState(state, r, nil, str[length+l:])
|
|
||||||
|
|
||||||
if boundary != LineDontBreak {
|
|
||||||
return str[:length], str[length:], boundary == LineMustBreak, state
|
|
||||||
}
|
|
||||||
|
|
||||||
length += l
|
|
||||||
if len(str) <= length {
|
|
||||||
return str, "", true, lbAny // LB3.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasTrailingLineBreak returns true if the last rune in the given byte slice is
|
|
||||||
// one of the hard line break code points defined in LB4 and LB5 of [UAX #14].
|
|
||||||
//
|
|
||||||
// [UAX #14]: https://www.unicode.org/reports/tr14/#Algorithm
|
|
||||||
func HasTrailingLineBreak(b []byte) bool {
|
|
||||||
r, _ := utf8.DecodeLastRune(b)
|
|
||||||
property, _ := propertyLineBreak(r)
|
|
||||||
return property == prBK || property == prCR || property == prLF || property == prNL
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasTrailingLineBreakInString is like [HasTrailingLineBreak] but for a string.
|
|
||||||
func HasTrailingLineBreakInString(str string) bool {
|
|
||||||
r, _ := utf8.DecodeLastRuneInString(str)
|
|
||||||
property, _ := propertyLineBreak(r)
|
|
||||||
return property == prBK || property == prCR || property == prLF || property == prNL
|
|
||||||
}
|
|
||||||
3554
vendor/github.com/rivo/uniseg/lineproperties.go
generated
vendored
3554
vendor/github.com/rivo/uniseg/lineproperties.go
generated
vendored
File diff suppressed because it is too large
Load diff
626
vendor/github.com/rivo/uniseg/linerules.go
generated
vendored
626
vendor/github.com/rivo/uniseg/linerules.go
generated
vendored
|
|
@ -1,626 +0,0 @@
|
||||||
package uniseg
|
|
||||||
|
|
||||||
import "unicode/utf8"
|
|
||||||
|
|
||||||
// The states of the line break parser.
|
|
||||||
const (
|
|
||||||
lbAny = iota
|
|
||||||
lbBK
|
|
||||||
lbCR
|
|
||||||
lbLF
|
|
||||||
lbNL
|
|
||||||
lbSP
|
|
||||||
lbZW
|
|
||||||
lbWJ
|
|
||||||
lbGL
|
|
||||||
lbBA
|
|
||||||
lbHY
|
|
||||||
lbCL
|
|
||||||
lbCP
|
|
||||||
lbEX
|
|
||||||
lbIS
|
|
||||||
lbSY
|
|
||||||
lbOP
|
|
||||||
lbQU
|
|
||||||
lbQUSP
|
|
||||||
lbNS
|
|
||||||
lbCLCPSP
|
|
||||||
lbB2
|
|
||||||
lbB2SP
|
|
||||||
lbCB
|
|
||||||
lbBB
|
|
||||||
lbLB21a
|
|
||||||
lbHL
|
|
||||||
lbAL
|
|
||||||
lbNU
|
|
||||||
lbPR
|
|
||||||
lbEB
|
|
||||||
lbIDEM
|
|
||||||
lbNUNU
|
|
||||||
lbNUSY
|
|
||||||
lbNUIS
|
|
||||||
lbNUCL
|
|
||||||
lbNUCP
|
|
||||||
lbPO
|
|
||||||
lbJL
|
|
||||||
lbJV
|
|
||||||
lbJT
|
|
||||||
lbH2
|
|
||||||
lbH3
|
|
||||||
lbOddRI
|
|
||||||
lbEvenRI
|
|
||||||
lbExtPicCn
|
|
||||||
lbZWJBit = 64
|
|
||||||
lbCPeaFWHBit = 128
|
|
||||||
)
|
|
||||||
|
|
||||||
// These constants define whether a given text may be broken into the next line.
|
|
||||||
// If the break is optional (LineCanBreak), you may choose to break or not based
|
|
||||||
// on your own criteria, for example, if the text has reached the available
|
|
||||||
// width.
|
|
||||||
const (
|
|
||||||
LineDontBreak = iota // You may not break the line here.
|
|
||||||
LineCanBreak // You may or may not break the line here.
|
|
||||||
LineMustBreak // You must break the line here.
|
|
||||||
)
|
|
||||||
|
|
||||||
// lbTransitions implements the line break parser's state transitions. It's
|
|
||||||
// anologous to [grTransitions], see comments there for details.
|
|
||||||
//
|
|
||||||
// Unicode version 15.0.0.
|
|
||||||
func lbTransitions(state, prop int) (newState, lineBreak, rule int) {
|
|
||||||
switch uint64(state) | uint64(prop)<<32 {
|
|
||||||
// LB4.
|
|
||||||
case lbBK | prAny<<32:
|
|
||||||
return lbAny, LineMustBreak, 40
|
|
||||||
|
|
||||||
// LB5.
|
|
||||||
case lbCR | prLF<<32:
|
|
||||||
return lbLF, LineDontBreak, 50
|
|
||||||
case lbCR | prAny<<32:
|
|
||||||
return lbAny, LineMustBreak, 50
|
|
||||||
case lbLF | prAny<<32:
|
|
||||||
return lbAny, LineMustBreak, 50
|
|
||||||
case lbNL | prAny<<32:
|
|
||||||
return lbAny, LineMustBreak, 50
|
|
||||||
|
|
||||||
// LB6.
|
|
||||||
case lbAny | prBK<<32:
|
|
||||||
return lbBK, LineDontBreak, 60
|
|
||||||
case lbAny | prCR<<32:
|
|
||||||
return lbCR, LineDontBreak, 60
|
|
||||||
case lbAny | prLF<<32:
|
|
||||||
return lbLF, LineDontBreak, 60
|
|
||||||
case lbAny | prNL<<32:
|
|
||||||
return lbNL, LineDontBreak, 60
|
|
||||||
|
|
||||||
// LB7.
|
|
||||||
case lbAny | prSP<<32:
|
|
||||||
return lbSP, LineDontBreak, 70
|
|
||||||
case lbAny | prZW<<32:
|
|
||||||
return lbZW, LineDontBreak, 70
|
|
||||||
|
|
||||||
// LB8.
|
|
||||||
case lbZW | prSP<<32:
|
|
||||||
return lbZW, LineDontBreak, 70
|
|
||||||
case lbZW | prAny<<32:
|
|
||||||
return lbAny, LineCanBreak, 80
|
|
||||||
|
|
||||||
// LB11.
|
|
||||||
case lbAny | prWJ<<32:
|
|
||||||
return lbWJ, LineDontBreak, 110
|
|
||||||
case lbWJ | prAny<<32:
|
|
||||||
return lbAny, LineDontBreak, 110
|
|
||||||
|
|
||||||
// LB12.
|
|
||||||
case lbAny | prGL<<32:
|
|
||||||
return lbGL, LineCanBreak, 310
|
|
||||||
case lbGL | prAny<<32:
|
|
||||||
return lbAny, LineDontBreak, 120
|
|
||||||
|
|
||||||
// LB13 (simple transitions).
|
|
||||||
case lbAny | prCL<<32:
|
|
||||||
return lbCL, LineCanBreak, 310
|
|
||||||
case lbAny | prCP<<32:
|
|
||||||
return lbCP, LineCanBreak, 310
|
|
||||||
case lbAny | prEX<<32:
|
|
||||||
return lbEX, LineDontBreak, 130
|
|
||||||
case lbAny | prIS<<32:
|
|
||||||
return lbIS, LineCanBreak, 310
|
|
||||||
case lbAny | prSY<<32:
|
|
||||||
return lbSY, LineCanBreak, 310
|
|
||||||
|
|
||||||
// LB14.
|
|
||||||
case lbAny | prOP<<32:
|
|
||||||
return lbOP, LineCanBreak, 310
|
|
||||||
case lbOP | prSP<<32:
|
|
||||||
return lbOP, LineDontBreak, 70
|
|
||||||
case lbOP | prAny<<32:
|
|
||||||
return lbAny, LineDontBreak, 140
|
|
||||||
|
|
||||||
// LB15.
|
|
||||||
case lbQU | prSP<<32:
|
|
||||||
return lbQUSP, LineDontBreak, 70
|
|
||||||
case lbQU | prOP<<32:
|
|
||||||
return lbOP, LineDontBreak, 150
|
|
||||||
case lbQUSP | prOP<<32:
|
|
||||||
return lbOP, LineDontBreak, 150
|
|
||||||
|
|
||||||
// LB16.
|
|
||||||
case lbCL | prSP<<32:
|
|
||||||
return lbCLCPSP, LineDontBreak, 70
|
|
||||||
case lbNUCL | prSP<<32:
|
|
||||||
return lbCLCPSP, LineDontBreak, 70
|
|
||||||
case lbCP | prSP<<32:
|
|
||||||
return lbCLCPSP, LineDontBreak, 70
|
|
||||||
case lbNUCP | prSP<<32:
|
|
||||||
return lbCLCPSP, LineDontBreak, 70
|
|
||||||
case lbCL | prNS<<32:
|
|
||||||
return lbNS, LineDontBreak, 160
|
|
||||||
case lbNUCL | prNS<<32:
|
|
||||||
return lbNS, LineDontBreak, 160
|
|
||||||
case lbCP | prNS<<32:
|
|
||||||
return lbNS, LineDontBreak, 160
|
|
||||||
case lbNUCP | prNS<<32:
|
|
||||||
return lbNS, LineDontBreak, 160
|
|
||||||
case lbCLCPSP | prNS<<32:
|
|
||||||
return lbNS, LineDontBreak, 160
|
|
||||||
|
|
||||||
// LB17.
|
|
||||||
case lbAny | prB2<<32:
|
|
||||||
return lbB2, LineCanBreak, 310
|
|
||||||
case lbB2 | prSP<<32:
|
|
||||||
return lbB2SP, LineDontBreak, 70
|
|
||||||
case lbB2 | prB2<<32:
|
|
||||||
return lbB2, LineDontBreak, 170
|
|
||||||
case lbB2SP | prB2<<32:
|
|
||||||
return lbB2, LineDontBreak, 170
|
|
||||||
|
|
||||||
// LB18.
|
|
||||||
case lbSP | prAny<<32:
|
|
||||||
return lbAny, LineCanBreak, 180
|
|
||||||
case lbQUSP | prAny<<32:
|
|
||||||
return lbAny, LineCanBreak, 180
|
|
||||||
case lbCLCPSP | prAny<<32:
|
|
||||||
return lbAny, LineCanBreak, 180
|
|
||||||
case lbB2SP | prAny<<32:
|
|
||||||
return lbAny, LineCanBreak, 180
|
|
||||||
|
|
||||||
// LB19.
|
|
||||||
case lbAny | prQU<<32:
|
|
||||||
return lbQU, LineDontBreak, 190
|
|
||||||
case lbQU | prAny<<32:
|
|
||||||
return lbAny, LineDontBreak, 190
|
|
||||||
|
|
||||||
// LB20.
|
|
||||||
case lbAny | prCB<<32:
|
|
||||||
return lbCB, LineCanBreak, 200
|
|
||||||
case lbCB | prAny<<32:
|
|
||||||
return lbAny, LineCanBreak, 200
|
|
||||||
|
|
||||||
// LB21.
|
|
||||||
case lbAny | prBA<<32:
|
|
||||||
return lbBA, LineDontBreak, 210
|
|
||||||
case lbAny | prHY<<32:
|
|
||||||
return lbHY, LineDontBreak, 210
|
|
||||||
case lbAny | prNS<<32:
|
|
||||||
return lbNS, LineDontBreak, 210
|
|
||||||
case lbAny | prBB<<32:
|
|
||||||
return lbBB, LineCanBreak, 310
|
|
||||||
case lbBB | prAny<<32:
|
|
||||||
return lbAny, LineDontBreak, 210
|
|
||||||
|
|
||||||
// LB21a.
|
|
||||||
case lbAny | prHL<<32:
|
|
||||||
return lbHL, LineCanBreak, 310
|
|
||||||
case lbHL | prHY<<32:
|
|
||||||
return lbLB21a, LineDontBreak, 210
|
|
||||||
case lbHL | prBA<<32:
|
|
||||||
return lbLB21a, LineDontBreak, 210
|
|
||||||
case lbLB21a | prAny<<32:
|
|
||||||
return lbAny, LineDontBreak, 211
|
|
||||||
|
|
||||||
// LB21b.
|
|
||||||
case lbSY | prHL<<32:
|
|
||||||
return lbHL, LineDontBreak, 212
|
|
||||||
case lbNUSY | prHL<<32:
|
|
||||||
return lbHL, LineDontBreak, 212
|
|
||||||
|
|
||||||
// LB22.
|
|
||||||
case lbAny | prIN<<32:
|
|
||||||
return lbAny, LineDontBreak, 220
|
|
||||||
|
|
||||||
// LB23.
|
|
||||||
case lbAny | prAL<<32:
|
|
||||||
return lbAL, LineCanBreak, 310
|
|
||||||
case lbAny | prNU<<32:
|
|
||||||
return lbNU, LineCanBreak, 310
|
|
||||||
case lbAL | prNU<<32:
|
|
||||||
return lbNU, LineDontBreak, 230
|
|
||||||
case lbHL | prNU<<32:
|
|
||||||
return lbNU, LineDontBreak, 230
|
|
||||||
case lbNU | prAL<<32:
|
|
||||||
return lbAL, LineDontBreak, 230
|
|
||||||
case lbNU | prHL<<32:
|
|
||||||
return lbHL, LineDontBreak, 230
|
|
||||||
case lbNUNU | prAL<<32:
|
|
||||||
return lbAL, LineDontBreak, 230
|
|
||||||
case lbNUNU | prHL<<32:
|
|
||||||
return lbHL, LineDontBreak, 230
|
|
||||||
|
|
||||||
// LB23a.
|
|
||||||
case lbAny | prPR<<32:
|
|
||||||
return lbPR, LineCanBreak, 310
|
|
||||||
case lbAny | prID<<32:
|
|
||||||
return lbIDEM, LineCanBreak, 310
|
|
||||||
case lbAny | prEB<<32:
|
|
||||||
return lbEB, LineCanBreak, 310
|
|
||||||
case lbAny | prEM<<32:
|
|
||||||
return lbIDEM, LineCanBreak, 310
|
|
||||||
case lbPR | prID<<32:
|
|
||||||
return lbIDEM, LineDontBreak, 231
|
|
||||||
case lbPR | prEB<<32:
|
|
||||||
return lbEB, LineDontBreak, 231
|
|
||||||
case lbPR | prEM<<32:
|
|
||||||
return lbIDEM, LineDontBreak, 231
|
|
||||||
case lbIDEM | prPO<<32:
|
|
||||||
return lbPO, LineDontBreak, 231
|
|
||||||
case lbEB | prPO<<32:
|
|
||||||
return lbPO, LineDontBreak, 231
|
|
||||||
|
|
||||||
// LB24.
|
|
||||||
case lbAny | prPO<<32:
|
|
||||||
return lbPO, LineCanBreak, 310
|
|
||||||
case lbPR | prAL<<32:
|
|
||||||
return lbAL, LineDontBreak, 240
|
|
||||||
case lbPR | prHL<<32:
|
|
||||||
return lbHL, LineDontBreak, 240
|
|
||||||
case lbPO | prAL<<32:
|
|
||||||
return lbAL, LineDontBreak, 240
|
|
||||||
case lbPO | prHL<<32:
|
|
||||||
return lbHL, LineDontBreak, 240
|
|
||||||
case lbAL | prPR<<32:
|
|
||||||
return lbPR, LineDontBreak, 240
|
|
||||||
case lbAL | prPO<<32:
|
|
||||||
return lbPO, LineDontBreak, 240
|
|
||||||
case lbHL | prPR<<32:
|
|
||||||
return lbPR, LineDontBreak, 240
|
|
||||||
case lbHL | prPO<<32:
|
|
||||||
return lbPO, LineDontBreak, 240
|
|
||||||
|
|
||||||
// LB25 (simple transitions).
|
|
||||||
case lbPR | prNU<<32:
|
|
||||||
return lbNU, LineDontBreak, 250
|
|
||||||
case lbPO | prNU<<32:
|
|
||||||
return lbNU, LineDontBreak, 250
|
|
||||||
case lbOP | prNU<<32:
|
|
||||||
return lbNU, LineDontBreak, 250
|
|
||||||
case lbHY | prNU<<32:
|
|
||||||
return lbNU, LineDontBreak, 250
|
|
||||||
case lbNU | prNU<<32:
|
|
||||||
return lbNUNU, LineDontBreak, 250
|
|
||||||
case lbNU | prSY<<32:
|
|
||||||
return lbNUSY, LineDontBreak, 250
|
|
||||||
case lbNU | prIS<<32:
|
|
||||||
return lbNUIS, LineDontBreak, 250
|
|
||||||
case lbNUNU | prNU<<32:
|
|
||||||
return lbNUNU, LineDontBreak, 250
|
|
||||||
case lbNUNU | prSY<<32:
|
|
||||||
return lbNUSY, LineDontBreak, 250
|
|
||||||
case lbNUNU | prIS<<32:
|
|
||||||
return lbNUIS, LineDontBreak, 250
|
|
||||||
case lbNUSY | prNU<<32:
|
|
||||||
return lbNUNU, LineDontBreak, 250
|
|
||||||
case lbNUSY | prSY<<32:
|
|
||||||
return lbNUSY, LineDontBreak, 250
|
|
||||||
case lbNUSY | prIS<<32:
|
|
||||||
return lbNUIS, LineDontBreak, 250
|
|
||||||
case lbNUIS | prNU<<32:
|
|
||||||
return lbNUNU, LineDontBreak, 250
|
|
||||||
case lbNUIS | prSY<<32:
|
|
||||||
return lbNUSY, LineDontBreak, 250
|
|
||||||
case lbNUIS | prIS<<32:
|
|
||||||
return lbNUIS, LineDontBreak, 250
|
|
||||||
case lbNU | prCL<<32:
|
|
||||||
return lbNUCL, LineDontBreak, 250
|
|
||||||
case lbNU | prCP<<32:
|
|
||||||
return lbNUCP, LineDontBreak, 250
|
|
||||||
case lbNUNU | prCL<<32:
|
|
||||||
return lbNUCL, LineDontBreak, 250
|
|
||||||
case lbNUNU | prCP<<32:
|
|
||||||
return lbNUCP, LineDontBreak, 250
|
|
||||||
case lbNUSY | prCL<<32:
|
|
||||||
return lbNUCL, LineDontBreak, 250
|
|
||||||
case lbNUSY | prCP<<32:
|
|
||||||
return lbNUCP, LineDontBreak, 250
|
|
||||||
case lbNUIS | prCL<<32:
|
|
||||||
return lbNUCL, LineDontBreak, 250
|
|
||||||
case lbNUIS | prCP<<32:
|
|
||||||
return lbNUCP, LineDontBreak, 250
|
|
||||||
case lbNU | prPO<<32:
|
|
||||||
return lbPO, LineDontBreak, 250
|
|
||||||
case lbNUNU | prPO<<32:
|
|
||||||
return lbPO, LineDontBreak, 250
|
|
||||||
case lbNUSY | prPO<<32:
|
|
||||||
return lbPO, LineDontBreak, 250
|
|
||||||
case lbNUIS | prPO<<32:
|
|
||||||
return lbPO, LineDontBreak, 250
|
|
||||||
case lbNUCL | prPO<<32:
|
|
||||||
return lbPO, LineDontBreak, 250
|
|
||||||
case lbNUCP | prPO<<32:
|
|
||||||
return lbPO, LineDontBreak, 250
|
|
||||||
case lbNU | prPR<<32:
|
|
||||||
return lbPR, LineDontBreak, 250
|
|
||||||
case lbNUNU | prPR<<32:
|
|
||||||
return lbPR, LineDontBreak, 250
|
|
||||||
case lbNUSY | prPR<<32:
|
|
||||||
return lbPR, LineDontBreak, 250
|
|
||||||
case lbNUIS | prPR<<32:
|
|
||||||
return lbPR, LineDontBreak, 250
|
|
||||||
case lbNUCL | prPR<<32:
|
|
||||||
return lbPR, LineDontBreak, 250
|
|
||||||
case lbNUCP | prPR<<32:
|
|
||||||
return lbPR, LineDontBreak, 250
|
|
||||||
|
|
||||||
// LB26.
|
|
||||||
case lbAny | prJL<<32:
|
|
||||||
return lbJL, LineCanBreak, 310
|
|
||||||
case lbAny | prJV<<32:
|
|
||||||
return lbJV, LineCanBreak, 310
|
|
||||||
case lbAny | prJT<<32:
|
|
||||||
return lbJT, LineCanBreak, 310
|
|
||||||
case lbAny | prH2<<32:
|
|
||||||
return lbH2, LineCanBreak, 310
|
|
||||||
case lbAny | prH3<<32:
|
|
||||||
return lbH3, LineCanBreak, 310
|
|
||||||
case lbJL | prJL<<32:
|
|
||||||
return lbJL, LineDontBreak, 260
|
|
||||||
case lbJL | prJV<<32:
|
|
||||||
return lbJV, LineDontBreak, 260
|
|
||||||
case lbJL | prH2<<32:
|
|
||||||
return lbH2, LineDontBreak, 260
|
|
||||||
case lbJL | prH3<<32:
|
|
||||||
return lbH3, LineDontBreak, 260
|
|
||||||
case lbJV | prJV<<32:
|
|
||||||
return lbJV, LineDontBreak, 260
|
|
||||||
case lbJV | prJT<<32:
|
|
||||||
return lbJT, LineDontBreak, 260
|
|
||||||
case lbH2 | prJV<<32:
|
|
||||||
return lbJV, LineDontBreak, 260
|
|
||||||
case lbH2 | prJT<<32:
|
|
||||||
return lbJT, LineDontBreak, 260
|
|
||||||
case lbJT | prJT<<32:
|
|
||||||
return lbJT, LineDontBreak, 260
|
|
||||||
case lbH3 | prJT<<32:
|
|
||||||
return lbJT, LineDontBreak, 260
|
|
||||||
|
|
||||||
// LB27.
|
|
||||||
case lbJL | prPO<<32:
|
|
||||||
return lbPO, LineDontBreak, 270
|
|
||||||
case lbJV | prPO<<32:
|
|
||||||
return lbPO, LineDontBreak, 270
|
|
||||||
case lbJT | prPO<<32:
|
|
||||||
return lbPO, LineDontBreak, 270
|
|
||||||
case lbH2 | prPO<<32:
|
|
||||||
return lbPO, LineDontBreak, 270
|
|
||||||
case lbH3 | prPO<<32:
|
|
||||||
return lbPO, LineDontBreak, 270
|
|
||||||
case lbPR | prJL<<32:
|
|
||||||
return lbJL, LineDontBreak, 270
|
|
||||||
case lbPR | prJV<<32:
|
|
||||||
return lbJV, LineDontBreak, 270
|
|
||||||
case lbPR | prJT<<32:
|
|
||||||
return lbJT, LineDontBreak, 270
|
|
||||||
case lbPR | prH2<<32:
|
|
||||||
return lbH2, LineDontBreak, 270
|
|
||||||
case lbPR | prH3<<32:
|
|
||||||
return lbH3, LineDontBreak, 270
|
|
||||||
|
|
||||||
// LB28.
|
|
||||||
case lbAL | prAL<<32:
|
|
||||||
return lbAL, LineDontBreak, 280
|
|
||||||
case lbAL | prHL<<32:
|
|
||||||
return lbHL, LineDontBreak, 280
|
|
||||||
case lbHL | prAL<<32:
|
|
||||||
return lbAL, LineDontBreak, 280
|
|
||||||
case lbHL | prHL<<32:
|
|
||||||
return lbHL, LineDontBreak, 280
|
|
||||||
|
|
||||||
// LB29.
|
|
||||||
case lbIS | prAL<<32:
|
|
||||||
return lbAL, LineDontBreak, 290
|
|
||||||
case lbIS | prHL<<32:
|
|
||||||
return lbHL, LineDontBreak, 290
|
|
||||||
case lbNUIS | prAL<<32:
|
|
||||||
return lbAL, LineDontBreak, 290
|
|
||||||
case lbNUIS | prHL<<32:
|
|
||||||
return lbHL, LineDontBreak, 290
|
|
||||||
|
|
||||||
default:
|
|
||||||
return -1, -1, -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// transitionLineBreakState determines the new state of the line break parser
|
|
||||||
// given the current state and the next code point. It also returns the type of
|
|
||||||
// line break: LineDontBreak, LineCanBreak, or LineMustBreak. If more than one
|
|
||||||
// code point is needed to determine the new state, the byte slice or the string
|
|
||||||
// starting after rune "r" can be used (whichever is not nil or empty) for
|
|
||||||
// further lookups.
|
|
||||||
func transitionLineBreakState(state int, r rune, b []byte, str string) (newState int, lineBreak int) {
|
|
||||||
// Determine the property of the next character.
|
|
||||||
nextProperty, generalCategory := propertyLineBreak(r)
|
|
||||||
|
|
||||||
// Prepare.
|
|
||||||
var forceNoBreak, isCPeaFWH bool
|
|
||||||
if state >= 0 && state&lbCPeaFWHBit != 0 {
|
|
||||||
isCPeaFWH = true // LB30: CP but ea is not F, W, or H.
|
|
||||||
state = state &^ lbCPeaFWHBit
|
|
||||||
}
|
|
||||||
if state >= 0 && state&lbZWJBit != 0 {
|
|
||||||
state = state &^ lbZWJBit // Extract zero-width joiner bit.
|
|
||||||
forceNoBreak = true // LB8a.
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
// Transition into LB30.
|
|
||||||
if newState == lbCP || newState == lbNUCP {
|
|
||||||
ea := propertyEastAsianWidth(r)
|
|
||||||
if ea != prF && ea != prW && ea != prH {
|
|
||||||
newState |= lbCPeaFWHBit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Override break.
|
|
||||||
if forceNoBreak {
|
|
||||||
lineBreak = LineDontBreak
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// LB1.
|
|
||||||
if nextProperty == prAI || nextProperty == prSG || nextProperty == prXX {
|
|
||||||
nextProperty = prAL
|
|
||||||
} else if nextProperty == prSA {
|
|
||||||
if generalCategory == gcMn || generalCategory == gcMc {
|
|
||||||
nextProperty = prCM
|
|
||||||
} else {
|
|
||||||
nextProperty = prAL
|
|
||||||
}
|
|
||||||
} else if nextProperty == prCJ {
|
|
||||||
nextProperty = prNS
|
|
||||||
}
|
|
||||||
|
|
||||||
// Combining marks.
|
|
||||||
if nextProperty == prZWJ || nextProperty == prCM {
|
|
||||||
var bit int
|
|
||||||
if nextProperty == prZWJ {
|
|
||||||
bit = lbZWJBit
|
|
||||||
}
|
|
||||||
mustBreakState := state < 0 || state == lbBK || state == lbCR || state == lbLF || state == lbNL
|
|
||||||
if !mustBreakState && state != lbSP && state != lbZW && state != lbQUSP && state != lbCLCPSP && state != lbB2SP {
|
|
||||||
// LB9.
|
|
||||||
return state | bit, LineDontBreak
|
|
||||||
} else {
|
|
||||||
// LB10.
|
|
||||||
if mustBreakState {
|
|
||||||
return lbAL | bit, LineMustBreak
|
|
||||||
}
|
|
||||||
return lbAL | bit, LineCanBreak
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the applicable transition in the table.
|
|
||||||
var rule int
|
|
||||||
newState, lineBreak, rule = lbTransitions(state, nextProperty)
|
|
||||||
if newState < 0 {
|
|
||||||
// No specific transition found. Try the less specific ones.
|
|
||||||
anyPropProp, anyPropLineBreak, anyPropRule := lbTransitions(state, prAny)
|
|
||||||
anyStateProp, anyStateLineBreak, anyStateRule := lbTransitions(lbAny, nextProperty)
|
|
||||||
if anyPropProp >= 0 && anyStateProp >= 0 {
|
|
||||||
// Both apply. We'll use a mix (see comments for grTransitions).
|
|
||||||
newState, lineBreak, rule = anyStateProp, anyStateLineBreak, anyStateRule
|
|
||||||
if anyPropRule < anyStateRule {
|
|
||||||
lineBreak, rule = anyPropLineBreak, anyPropRule
|
|
||||||
}
|
|
||||||
} else if anyPropProp >= 0 {
|
|
||||||
// We only have a specific state.
|
|
||||||
newState, lineBreak, rule = anyPropProp, anyPropLineBreak, anyPropRule
|
|
||||||
// This branch will probably never be reached because okAnyState will
|
|
||||||
// always be true given the current transition map. But we keep it here
|
|
||||||
// for future modifications to the transition map where this may not be
|
|
||||||
// true anymore.
|
|
||||||
} else if anyStateProp >= 0 {
|
|
||||||
// We only have a specific property.
|
|
||||||
newState, lineBreak, rule = anyStateProp, anyStateLineBreak, anyStateRule
|
|
||||||
} else {
|
|
||||||
// No known transition. LB31: ALL ÷ ALL.
|
|
||||||
newState, lineBreak, rule = lbAny, LineCanBreak, 310
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// LB12a.
|
|
||||||
if rule > 121 &&
|
|
||||||
nextProperty == prGL &&
|
|
||||||
(state != lbSP && state != lbBA && state != lbHY && state != lbLB21a && state != lbQUSP && state != lbCLCPSP && state != lbB2SP) {
|
|
||||||
return lbGL, LineDontBreak
|
|
||||||
}
|
|
||||||
|
|
||||||
// LB13.
|
|
||||||
if rule > 130 && state != lbNU && state != lbNUNU {
|
|
||||||
switch nextProperty {
|
|
||||||
case prCL:
|
|
||||||
return lbCL, LineDontBreak
|
|
||||||
case prCP:
|
|
||||||
return lbCP, LineDontBreak
|
|
||||||
case prIS:
|
|
||||||
return lbIS, LineDontBreak
|
|
||||||
case prSY:
|
|
||||||
return lbSY, LineDontBreak
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// LB25 (look ahead).
|
|
||||||
if rule > 250 &&
|
|
||||||
(state == lbPR || state == lbPO) &&
|
|
||||||
nextProperty == prOP || nextProperty == prHY {
|
|
||||||
var r rune
|
|
||||||
if b != nil { // Byte slice version.
|
|
||||||
r, _ = utf8.DecodeRune(b)
|
|
||||||
} else { // String version.
|
|
||||||
r, _ = utf8.DecodeRuneInString(str)
|
|
||||||
}
|
|
||||||
if r != utf8.RuneError {
|
|
||||||
pr, _ := propertyLineBreak(r)
|
|
||||||
if pr == prNU {
|
|
||||||
return lbNU, LineDontBreak
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// LB30 (part one).
|
|
||||||
if rule > 300 {
|
|
||||||
if (state == lbAL || state == lbHL || state == lbNU || state == lbNUNU) && nextProperty == prOP {
|
|
||||||
ea := propertyEastAsianWidth(r)
|
|
||||||
if ea != prF && ea != prW && ea != prH {
|
|
||||||
return lbOP, LineDontBreak
|
|
||||||
}
|
|
||||||
} else if isCPeaFWH {
|
|
||||||
switch nextProperty {
|
|
||||||
case prAL:
|
|
||||||
return lbAL, LineDontBreak
|
|
||||||
case prHL:
|
|
||||||
return lbHL, LineDontBreak
|
|
||||||
case prNU:
|
|
||||||
return lbNU, LineDontBreak
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// LB30a.
|
|
||||||
if newState == lbAny && nextProperty == prRI {
|
|
||||||
if state != lbOddRI && state != lbEvenRI { // Includes state == -1.
|
|
||||||
// Transition into the first RI.
|
|
||||||
return lbOddRI, lineBreak
|
|
||||||
}
|
|
||||||
if state == lbOddRI {
|
|
||||||
// Don't break pairs of Regional Indicators.
|
|
||||||
return lbEvenRI, LineDontBreak
|
|
||||||
}
|
|
||||||
return lbOddRI, lineBreak
|
|
||||||
}
|
|
||||||
|
|
||||||
// LB30b.
|
|
||||||
if rule > 302 {
|
|
||||||
if nextProperty == prEM {
|
|
||||||
if state == lbEB || state == lbExtPicCn {
|
|
||||||
return prAny, LineDontBreak
|
|
||||||
}
|
|
||||||
}
|
|
||||||
graphemeProperty := propertyGraphemes(r)
|
|
||||||
if graphemeProperty == prExtendedPictographic && generalCategory == gcCn {
|
|
||||||
return lbExtPicCn, LineCanBreak
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
208
vendor/github.com/rivo/uniseg/properties.go
generated
vendored
208
vendor/github.com/rivo/uniseg/properties.go
generated
vendored
|
|
@ -1,208 +0,0 @@
|
||||||
package uniseg
|
|
||||||
|
|
||||||
// The Unicode properties as used in the various parsers. Only the ones needed
|
|
||||||
// in the context of this package are included.
|
|
||||||
const (
|
|
||||||
prXX = 0 // Same as prAny.
|
|
||||||
prAny = iota // prAny must be 0.
|
|
||||||
prPrepend // Grapheme properties must come first, to reduce the number of bits stored in the state vector.
|
|
||||||
prCR
|
|
||||||
prLF
|
|
||||||
prControl
|
|
||||||
prExtend
|
|
||||||
prRegionalIndicator
|
|
||||||
prSpacingMark
|
|
||||||
prL
|
|
||||||
prV
|
|
||||||
prT
|
|
||||||
prLV
|
|
||||||
prLVT
|
|
||||||
prZWJ
|
|
||||||
prExtendedPictographic
|
|
||||||
prNewline
|
|
||||||
prWSegSpace
|
|
||||||
prDoubleQuote
|
|
||||||
prSingleQuote
|
|
||||||
prMidNumLet
|
|
||||||
prNumeric
|
|
||||||
prMidLetter
|
|
||||||
prMidNum
|
|
||||||
prExtendNumLet
|
|
||||||
prALetter
|
|
||||||
prFormat
|
|
||||||
prHebrewLetter
|
|
||||||
prKatakana
|
|
||||||
prSp
|
|
||||||
prSTerm
|
|
||||||
prClose
|
|
||||||
prSContinue
|
|
||||||
prATerm
|
|
||||||
prUpper
|
|
||||||
prLower
|
|
||||||
prSep
|
|
||||||
prOLetter
|
|
||||||
prCM
|
|
||||||
prBA
|
|
||||||
prBK
|
|
||||||
prSP
|
|
||||||
prEX
|
|
||||||
prQU
|
|
||||||
prAL
|
|
||||||
prPR
|
|
||||||
prPO
|
|
||||||
prOP
|
|
||||||
prCP
|
|
||||||
prIS
|
|
||||||
prHY
|
|
||||||
prSY
|
|
||||||
prNU
|
|
||||||
prCL
|
|
||||||
prNL
|
|
||||||
prGL
|
|
||||||
prAI
|
|
||||||
prBB
|
|
||||||
prHL
|
|
||||||
prSA
|
|
||||||
prJL
|
|
||||||
prJV
|
|
||||||
prJT
|
|
||||||
prNS
|
|
||||||
prZW
|
|
||||||
prB2
|
|
||||||
prIN
|
|
||||||
prWJ
|
|
||||||
prID
|
|
||||||
prEB
|
|
||||||
prCJ
|
|
||||||
prH2
|
|
||||||
prH3
|
|
||||||
prSG
|
|
||||||
prCB
|
|
||||||
prRI
|
|
||||||
prEM
|
|
||||||
prN
|
|
||||||
prNa
|
|
||||||
prA
|
|
||||||
prW
|
|
||||||
prH
|
|
||||||
prF
|
|
||||||
prEmojiPresentation
|
|
||||||
)
|
|
||||||
|
|
||||||
// Unicode General Categories. Only the ones needed in the context of this
|
|
||||||
// package are included.
|
|
||||||
const (
|
|
||||||
gcNone = iota // gcNone must be 0.
|
|
||||||
gcCc
|
|
||||||
gcZs
|
|
||||||
gcPo
|
|
||||||
gcSc
|
|
||||||
gcPs
|
|
||||||
gcPe
|
|
||||||
gcSm
|
|
||||||
gcPd
|
|
||||||
gcNd
|
|
||||||
gcLu
|
|
||||||
gcSk
|
|
||||||
gcPc
|
|
||||||
gcLl
|
|
||||||
gcSo
|
|
||||||
gcLo
|
|
||||||
gcPi
|
|
||||||
gcCf
|
|
||||||
gcNo
|
|
||||||
gcPf
|
|
||||||
gcLC
|
|
||||||
gcLm
|
|
||||||
gcMn
|
|
||||||
gcMe
|
|
||||||
gcMc
|
|
||||||
gcNl
|
|
||||||
gcZl
|
|
||||||
gcZp
|
|
||||||
gcCn
|
|
||||||
gcCs
|
|
||||||
gcCo
|
|
||||||
)
|
|
||||||
|
|
||||||
// Special code points.
|
|
||||||
const (
|
|
||||||
vs15 = 0xfe0e // Variation Selector-15 (text presentation)
|
|
||||||
vs16 = 0xfe0f // Variation Selector-16 (emoji presentation)
|
|
||||||
)
|
|
||||||
|
|
||||||
// propertySearch performs a binary search on a property slice and returns the
|
|
||||||
// entry whose range (start = first array element, end = second array element)
|
|
||||||
// includes r, or an array of 0's if no such entry was found.
|
|
||||||
func propertySearch[E interface{ [3]int | [4]int }](dictionary []E, r rune) (result E) {
|
|
||||||
// Run a binary search.
|
|
||||||
from := 0
|
|
||||||
to := len(dictionary)
|
|
||||||
for to > from {
|
|
||||||
middle := (from + to) / 2
|
|
||||||
cpRange := dictionary[middle]
|
|
||||||
if int(r) < cpRange[0] {
|
|
||||||
to = middle
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if int(r) > cpRange[1] {
|
|
||||||
from = middle + 1
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return cpRange
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// property returns the Unicode property value (see constants above) of the
|
|
||||||
// given code point.
|
|
||||||
func property(dictionary [][3]int, r rune) int {
|
|
||||||
return propertySearch(dictionary, r)[2]
|
|
||||||
}
|
|
||||||
|
|
||||||
// propertyLineBreak returns the Unicode property value and General Category
|
|
||||||
// (see constants above) of the given code point, as listed in the line break
|
|
||||||
// code points table, while fast tracking ASCII digits and letters.
|
|
||||||
func propertyLineBreak(r rune) (property, generalCategory int) {
|
|
||||||
if r >= 'a' && r <= 'z' {
|
|
||||||
return prAL, gcLl
|
|
||||||
}
|
|
||||||
if r >= 'A' && r <= 'Z' {
|
|
||||||
return prAL, gcLu
|
|
||||||
}
|
|
||||||
if r >= '0' && r <= '9' {
|
|
||||||
return prNU, gcNd
|
|
||||||
}
|
|
||||||
entry := propertySearch(lineBreakCodePoints, r)
|
|
||||||
return entry[2], entry[3]
|
|
||||||
}
|
|
||||||
|
|
||||||
// propertyGraphemes returns the Unicode grapheme cluster property value of the
|
|
||||||
// given code point while fast tracking ASCII characters.
|
|
||||||
func propertyGraphemes(r rune) int {
|
|
||||||
if r >= 0x20 && r <= 0x7e {
|
|
||||||
return prAny
|
|
||||||
}
|
|
||||||
if r == 0x0a {
|
|
||||||
return prLF
|
|
||||||
}
|
|
||||||
if r == 0x0d {
|
|
||||||
return prCR
|
|
||||||
}
|
|
||||||
if r >= 0 && r <= 0x1f || r == 0x7f {
|
|
||||||
return prControl
|
|
||||||
}
|
|
||||||
return property(graphemeCodePoints, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// propertyEastAsianWidth returns the Unicode East Asian Width property value of
|
|
||||||
// the given code point while fast tracking ASCII characters.
|
|
||||||
func propertyEastAsianWidth(r rune) int {
|
|
||||||
if r >= 0x20 && r <= 0x7e {
|
|
||||||
return prNa
|
|
||||||
}
|
|
||||||
if r >= 0 && r <= 0x1f || r == 0x7f {
|
|
||||||
return prN
|
|
||||||
}
|
|
||||||
return property(eastAsianWidth, r)
|
|
||||||
}
|
|
||||||
90
vendor/github.com/rivo/uniseg/sentence.go
generated
vendored
90
vendor/github.com/rivo/uniseg/sentence.go
generated
vendored
|
|
@ -1,90 +0,0 @@
|
||||||
package uniseg
|
|
||||||
|
|
||||||
import "unicode/utf8"
|
|
||||||
|
|
||||||
// FirstSentence returns the first sentence found in the given byte slice
|
|
||||||
// according to the rules of [Unicode Standard Annex #29, Sentence Boundaries].
|
|
||||||
// This function can be called continuously to extract all sentences from a byte
|
|
||||||
// slice, as illustrated in the example below.
|
|
||||||
//
|
|
||||||
// If you don't know the current state, for example when calling the function
|
|
||||||
// for the first time, you must pass -1. For consecutive calls, pass the state
|
|
||||||
// and rest slice returned by the previous call.
|
|
||||||
//
|
|
||||||
// The "rest" slice is the sub-slice of the original byte slice "b" starting
|
|
||||||
// after the last byte of the identified sentence. If the length of the "rest"
|
|
||||||
// slice is 0, the entire byte slice "b" has been processed. The "sentence" byte
|
|
||||||
// slice is the sub-slice of the input slice containing the identified sentence.
|
|
||||||
//
|
|
||||||
// Given an empty byte slice "b", the function returns nil values.
|
|
||||||
//
|
|
||||||
// [Unicode Standard Annex #29, Sentence Boundaries]: http://unicode.org/reports/tr29/#Sentence_Boundaries
|
|
||||||
func FirstSentence(b []byte, state int) (sentence, rest []byte, newState int) {
|
|
||||||
// An empty byte slice returns nothing.
|
|
||||||
if len(b) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the first rune.
|
|
||||||
r, length := utf8.DecodeRune(b)
|
|
||||||
if len(b) <= length { // If we're already past the end, there is nothing else to parse.
|
|
||||||
return b, nil, sbAny
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we don't know the state, determine it now.
|
|
||||||
if state < 0 {
|
|
||||||
state, _ = transitionSentenceBreakState(state, r, b[length:], "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transition until we find a boundary.
|
|
||||||
var boundary bool
|
|
||||||
for {
|
|
||||||
r, l := utf8.DecodeRune(b[length:])
|
|
||||||
state, boundary = transitionSentenceBreakState(state, r, b[length+l:], "")
|
|
||||||
|
|
||||||
if boundary {
|
|
||||||
return b[:length], b[length:], state
|
|
||||||
}
|
|
||||||
|
|
||||||
length += l
|
|
||||||
if len(b) <= length {
|
|
||||||
return b, nil, sbAny
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FirstSentenceInString is like [FirstSentence] but its input and outputs are
|
|
||||||
// strings.
|
|
||||||
func FirstSentenceInString(str string, state int) (sentence, rest string, newState int) {
|
|
||||||
// An empty byte slice returns nothing.
|
|
||||||
if len(str) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the first rune.
|
|
||||||
r, length := utf8.DecodeRuneInString(str)
|
|
||||||
if len(str) <= length { // If we're already past the end, there is nothing else to parse.
|
|
||||||
return str, "", sbAny
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we don't know the state, determine it now.
|
|
||||||
if state < 0 {
|
|
||||||
state, _ = transitionSentenceBreakState(state, r, nil, str[length:])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transition until we find a boundary.
|
|
||||||
var boundary bool
|
|
||||||
for {
|
|
||||||
r, l := utf8.DecodeRuneInString(str[length:])
|
|
||||||
state, boundary = transitionSentenceBreakState(state, r, nil, str[length+l:])
|
|
||||||
|
|
||||||
if boundary {
|
|
||||||
return str[:length], str[length:], state
|
|
||||||
}
|
|
||||||
|
|
||||||
length += l
|
|
||||||
if len(str) <= length {
|
|
||||||
return str, "", sbAny
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
2845
vendor/github.com/rivo/uniseg/sentenceproperties.go
generated
vendored
2845
vendor/github.com/rivo/uniseg/sentenceproperties.go
generated
vendored
File diff suppressed because it is too large
Load diff
276
vendor/github.com/rivo/uniseg/sentencerules.go
generated
vendored
276
vendor/github.com/rivo/uniseg/sentencerules.go
generated
vendored
|
|
@ -1,276 +0,0 @@
|
||||||
package uniseg
|
|
||||||
|
|
||||||
import "unicode/utf8"
|
|
||||||
|
|
||||||
// The states of the sentence break parser.
|
|
||||||
const (
|
|
||||||
sbAny = iota
|
|
||||||
sbCR
|
|
||||||
sbParaSep
|
|
||||||
sbATerm
|
|
||||||
sbUpper
|
|
||||||
sbLower
|
|
||||||
sbSB7
|
|
||||||
sbSB8Close
|
|
||||||
sbSB8Sp
|
|
||||||
sbSTerm
|
|
||||||
sbSB8aClose
|
|
||||||
sbSB8aSp
|
|
||||||
)
|
|
||||||
|
|
||||||
// sbTransitions implements the sentence break parser's state transitions. It's
|
|
||||||
// anologous to [grTransitions], see comments there for details.
|
|
||||||
//
|
|
||||||
// Unicode version 15.0.0.
|
|
||||||
func sbTransitions(state, prop int) (newState int, sentenceBreak bool, rule int) {
|
|
||||||
switch uint64(state) | uint64(prop)<<32 {
|
|
||||||
// SB3.
|
|
||||||
case sbAny | prCR<<32:
|
|
||||||
return sbCR, false, 9990
|
|
||||||
case sbCR | prLF<<32:
|
|
||||||
return sbParaSep, false, 30
|
|
||||||
|
|
||||||
// SB4.
|
|
||||||
case sbAny | prSep<<32:
|
|
||||||
return sbParaSep, false, 9990
|
|
||||||
case sbAny | prLF<<32:
|
|
||||||
return sbParaSep, false, 9990
|
|
||||||
case sbParaSep | prAny<<32:
|
|
||||||
return sbAny, true, 40
|
|
||||||
case sbCR | prAny<<32:
|
|
||||||
return sbAny, true, 40
|
|
||||||
|
|
||||||
// SB6.
|
|
||||||
case sbAny | prATerm<<32:
|
|
||||||
return sbATerm, false, 9990
|
|
||||||
case sbATerm | prNumeric<<32:
|
|
||||||
return sbAny, false, 60
|
|
||||||
case sbSB7 | prNumeric<<32:
|
|
||||||
return sbAny, false, 60 // Because ATerm also appears in SB7.
|
|
||||||
|
|
||||||
// SB7.
|
|
||||||
case sbAny | prUpper<<32:
|
|
||||||
return sbUpper, false, 9990
|
|
||||||
case sbAny | prLower<<32:
|
|
||||||
return sbLower, false, 9990
|
|
||||||
case sbUpper | prATerm<<32:
|
|
||||||
return sbSB7, false, 70
|
|
||||||
case sbLower | prATerm<<32:
|
|
||||||
return sbSB7, false, 70
|
|
||||||
case sbSB7 | prUpper<<32:
|
|
||||||
return sbUpper, false, 70
|
|
||||||
|
|
||||||
// SB8a.
|
|
||||||
case sbAny | prSTerm<<32:
|
|
||||||
return sbSTerm, false, 9990
|
|
||||||
case sbATerm | prSContinue<<32:
|
|
||||||
return sbAny, false, 81
|
|
||||||
case sbATerm | prATerm<<32:
|
|
||||||
return sbATerm, false, 81
|
|
||||||
case sbATerm | prSTerm<<32:
|
|
||||||
return sbSTerm, false, 81
|
|
||||||
case sbSB7 | prSContinue<<32:
|
|
||||||
return sbAny, false, 81
|
|
||||||
case sbSB7 | prATerm<<32:
|
|
||||||
return sbATerm, false, 81
|
|
||||||
case sbSB7 | prSTerm<<32:
|
|
||||||
return sbSTerm, false, 81
|
|
||||||
case sbSB8Close | prSContinue<<32:
|
|
||||||
return sbAny, false, 81
|
|
||||||
case sbSB8Close | prATerm<<32:
|
|
||||||
return sbATerm, false, 81
|
|
||||||
case sbSB8Close | prSTerm<<32:
|
|
||||||
return sbSTerm, false, 81
|
|
||||||
case sbSB8Sp | prSContinue<<32:
|
|
||||||
return sbAny, false, 81
|
|
||||||
case sbSB8Sp | prATerm<<32:
|
|
||||||
return sbATerm, false, 81
|
|
||||||
case sbSB8Sp | prSTerm<<32:
|
|
||||||
return sbSTerm, false, 81
|
|
||||||
case sbSTerm | prSContinue<<32:
|
|
||||||
return sbAny, false, 81
|
|
||||||
case sbSTerm | prATerm<<32:
|
|
||||||
return sbATerm, false, 81
|
|
||||||
case sbSTerm | prSTerm<<32:
|
|
||||||
return sbSTerm, false, 81
|
|
||||||
case sbSB8aClose | prSContinue<<32:
|
|
||||||
return sbAny, false, 81
|
|
||||||
case sbSB8aClose | prATerm<<32:
|
|
||||||
return sbATerm, false, 81
|
|
||||||
case sbSB8aClose | prSTerm<<32:
|
|
||||||
return sbSTerm, false, 81
|
|
||||||
case sbSB8aSp | prSContinue<<32:
|
|
||||||
return sbAny, false, 81
|
|
||||||
case sbSB8aSp | prATerm<<32:
|
|
||||||
return sbATerm, false, 81
|
|
||||||
case sbSB8aSp | prSTerm<<32:
|
|
||||||
return sbSTerm, false, 81
|
|
||||||
|
|
||||||
// SB9.
|
|
||||||
case sbATerm | prClose<<32:
|
|
||||||
return sbSB8Close, false, 90
|
|
||||||
case sbSB7 | prClose<<32:
|
|
||||||
return sbSB8Close, false, 90
|
|
||||||
case sbSB8Close | prClose<<32:
|
|
||||||
return sbSB8Close, false, 90
|
|
||||||
case sbATerm | prSp<<32:
|
|
||||||
return sbSB8Sp, false, 90
|
|
||||||
case sbSB7 | prSp<<32:
|
|
||||||
return sbSB8Sp, false, 90
|
|
||||||
case sbSB8Close | prSp<<32:
|
|
||||||
return sbSB8Sp, false, 90
|
|
||||||
case sbSTerm | prClose<<32:
|
|
||||||
return sbSB8aClose, false, 90
|
|
||||||
case sbSB8aClose | prClose<<32:
|
|
||||||
return sbSB8aClose, false, 90
|
|
||||||
case sbSTerm | prSp<<32:
|
|
||||||
return sbSB8aSp, false, 90
|
|
||||||
case sbSB8aClose | prSp<<32:
|
|
||||||
return sbSB8aSp, false, 90
|
|
||||||
case sbATerm | prSep<<32:
|
|
||||||
return sbParaSep, false, 90
|
|
||||||
case sbATerm | prCR<<32:
|
|
||||||
return sbParaSep, false, 90
|
|
||||||
case sbATerm | prLF<<32:
|
|
||||||
return sbParaSep, false, 90
|
|
||||||
case sbSB7 | prSep<<32:
|
|
||||||
return sbParaSep, false, 90
|
|
||||||
case sbSB7 | prCR<<32:
|
|
||||||
return sbParaSep, false, 90
|
|
||||||
case sbSB7 | prLF<<32:
|
|
||||||
return sbParaSep, false, 90
|
|
||||||
case sbSB8Close | prSep<<32:
|
|
||||||
return sbParaSep, false, 90
|
|
||||||
case sbSB8Close | prCR<<32:
|
|
||||||
return sbParaSep, false, 90
|
|
||||||
case sbSB8Close | prLF<<32:
|
|
||||||
return sbParaSep, false, 90
|
|
||||||
case sbSTerm | prSep<<32:
|
|
||||||
return sbParaSep, false, 90
|
|
||||||
case sbSTerm | prCR<<32:
|
|
||||||
return sbParaSep, false, 90
|
|
||||||
case sbSTerm | prLF<<32:
|
|
||||||
return sbParaSep, false, 90
|
|
||||||
case sbSB8aClose | prSep<<32:
|
|
||||||
return sbParaSep, false, 90
|
|
||||||
case sbSB8aClose | prCR<<32:
|
|
||||||
return sbParaSep, false, 90
|
|
||||||
case sbSB8aClose | prLF<<32:
|
|
||||||
return sbParaSep, false, 90
|
|
||||||
|
|
||||||
// SB10.
|
|
||||||
case sbSB8Sp | prSp<<32:
|
|
||||||
return sbSB8Sp, false, 100
|
|
||||||
case sbSB8aSp | prSp<<32:
|
|
||||||
return sbSB8aSp, false, 100
|
|
||||||
case sbSB8Sp | prSep<<32:
|
|
||||||
return sbParaSep, false, 100
|
|
||||||
case sbSB8Sp | prCR<<32:
|
|
||||||
return sbParaSep, false, 100
|
|
||||||
case sbSB8Sp | prLF<<32:
|
|
||||||
return sbParaSep, false, 100
|
|
||||||
|
|
||||||
// SB11.
|
|
||||||
case sbATerm | prAny<<32:
|
|
||||||
return sbAny, true, 110
|
|
||||||
case sbSB7 | prAny<<32:
|
|
||||||
return sbAny, true, 110
|
|
||||||
case sbSB8Close | prAny<<32:
|
|
||||||
return sbAny, true, 110
|
|
||||||
case sbSB8Sp | prAny<<32:
|
|
||||||
return sbAny, true, 110
|
|
||||||
case sbSTerm | prAny<<32:
|
|
||||||
return sbAny, true, 110
|
|
||||||
case sbSB8aClose | prAny<<32:
|
|
||||||
return sbAny, true, 110
|
|
||||||
case sbSB8aSp | prAny<<32:
|
|
||||||
return sbAny, true, 110
|
|
||||||
// We'll always break after ParaSep due to SB4.
|
|
||||||
|
|
||||||
default:
|
|
||||||
return -1, false, -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// transitionSentenceBreakState determines the new state of the sentence break
|
|
||||||
// parser given the current state and the next code point. It also returns
|
|
||||||
// whether a sentence boundary was detected. If more than one code point is
|
|
||||||
// needed to determine the new state, the byte slice or the string starting
|
|
||||||
// after rune "r" can be used (whichever is not nil or empty) for further
|
|
||||||
// lookups.
|
|
||||||
func transitionSentenceBreakState(state int, r rune, b []byte, str string) (newState int, sentenceBreak bool) {
|
|
||||||
// Determine the property of the next character.
|
|
||||||
nextProperty := property(sentenceBreakCodePoints, r)
|
|
||||||
|
|
||||||
// SB5 (Replacing Ignore Rules).
|
|
||||||
if nextProperty == prExtend || nextProperty == prFormat {
|
|
||||||
if state == sbParaSep || state == sbCR {
|
|
||||||
return sbAny, true // Make sure we don't apply SB5 to SB3 or SB4.
|
|
||||||
}
|
|
||||||
if state < 0 {
|
|
||||||
return sbAny, true // SB1.
|
|
||||||
}
|
|
||||||
return state, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the applicable transition in the table.
|
|
||||||
var rule int
|
|
||||||
newState, sentenceBreak, rule = sbTransitions(state, nextProperty)
|
|
||||||
if newState < 0 {
|
|
||||||
// No specific transition found. Try the less specific ones.
|
|
||||||
anyPropState, anyPropProp, anyPropRule := sbTransitions(state, prAny)
|
|
||||||
anyStateState, anyStateProp, anyStateRule := sbTransitions(sbAny, nextProperty)
|
|
||||||
if anyPropState >= 0 && anyStateState >= 0 {
|
|
||||||
// Both apply. We'll use a mix (see comments for grTransitions).
|
|
||||||
newState, sentenceBreak, rule = anyStateState, anyStateProp, anyStateRule
|
|
||||||
if anyPropRule < anyStateRule {
|
|
||||||
sentenceBreak, rule = anyPropProp, anyPropRule
|
|
||||||
}
|
|
||||||
} else if anyPropState >= 0 {
|
|
||||||
// We only have a specific state.
|
|
||||||
newState, sentenceBreak, rule = anyPropState, anyPropProp, anyPropRule
|
|
||||||
// This branch will probably never be reached because okAnyState will
|
|
||||||
// always be true given the current transition map. But we keep it here
|
|
||||||
// for future modifications to the transition map where this may not be
|
|
||||||
// true anymore.
|
|
||||||
} else if anyStateState >= 0 {
|
|
||||||
// We only have a specific property.
|
|
||||||
newState, sentenceBreak, rule = anyStateState, anyStateProp, anyStateRule
|
|
||||||
} else {
|
|
||||||
// No known transition. SB999: Any × Any.
|
|
||||||
newState, sentenceBreak, rule = sbAny, false, 9990
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SB8.
|
|
||||||
if rule > 80 && (state == sbATerm || state == sbSB8Close || state == sbSB8Sp || state == sbSB7) {
|
|
||||||
// Check the right side of the rule.
|
|
||||||
var length int
|
|
||||||
for nextProperty != prOLetter &&
|
|
||||||
nextProperty != prUpper &&
|
|
||||||
nextProperty != prLower &&
|
|
||||||
nextProperty != prSep &&
|
|
||||||
nextProperty != prCR &&
|
|
||||||
nextProperty != prLF &&
|
|
||||||
nextProperty != prATerm &&
|
|
||||||
nextProperty != prSTerm {
|
|
||||||
// Move on to the next rune.
|
|
||||||
if b != nil { // Byte slice version.
|
|
||||||
r, length = utf8.DecodeRune(b)
|
|
||||||
b = b[length:]
|
|
||||||
} else { // String version.
|
|
||||||
r, length = utf8.DecodeRuneInString(str)
|
|
||||||
str = str[length:]
|
|
||||||
}
|
|
||||||
if r == utf8.RuneError {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
nextProperty = property(sentenceBreakCodePoints, r)
|
|
||||||
}
|
|
||||||
if nextProperty == prLower {
|
|
||||||
return sbLower, false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
242
vendor/github.com/rivo/uniseg/step.go
generated
vendored
242
vendor/github.com/rivo/uniseg/step.go
generated
vendored
|
|
@ -1,242 +0,0 @@
|
||||||
package uniseg
|
|
||||||
|
|
||||||
import "unicode/utf8"
|
|
||||||
|
|
||||||
// The bit masks used to extract boundary information returned by [Step].
|
|
||||||
const (
|
|
||||||
MaskLine = 3
|
|
||||||
MaskWord = 4
|
|
||||||
MaskSentence = 8
|
|
||||||
)
|
|
||||||
|
|
||||||
// The number of bits to shift the boundary information returned by [Step] to
|
|
||||||
// obtain the monospace width of the grapheme cluster.
|
|
||||||
const ShiftWidth = 4
|
|
||||||
|
|
||||||
// The bit positions by which boundary flags are shifted by the [Step] function.
|
|
||||||
// These must correspond to the Mask constants.
|
|
||||||
const (
|
|
||||||
shiftWord = 2
|
|
||||||
shiftSentence = 3
|
|
||||||
// shiftwWidth is ShiftWidth above. No mask as these are always the remaining bits.
|
|
||||||
)
|
|
||||||
|
|
||||||
// The bit positions by which states are shifted by the [Step] function. These
|
|
||||||
// values must ensure state values defined for each of the boundary algorithms
|
|
||||||
// don't overlap (and that they all still fit in a single int). These must
|
|
||||||
// correspond to the Mask constants.
|
|
||||||
const (
|
|
||||||
shiftWordState = 4
|
|
||||||
shiftSentenceState = 9
|
|
||||||
shiftLineState = 13
|
|
||||||
shiftPropState = 21 // No mask as these are always the remaining bits.
|
|
||||||
)
|
|
||||||
|
|
||||||
// The bit mask used to extract the state returned by the [Step] function, after
|
|
||||||
// shifting. These values must correspond to the shift constants.
|
|
||||||
const (
|
|
||||||
maskGraphemeState = 0xf
|
|
||||||
maskWordState = 0x1f
|
|
||||||
maskSentenceState = 0xf
|
|
||||||
maskLineState = 0xff
|
|
||||||
)
|
|
||||||
|
|
||||||
// Step returns the first grapheme cluster (user-perceived character) found in
|
|
||||||
// the given byte slice. It also returns information about the boundary between
|
|
||||||
// that grapheme cluster and the one following it as well as the monospace width
|
|
||||||
// of the grapheme cluster. There are three types of boundary information: word
|
|
||||||
// boundaries, sentence boundaries, and line breaks. This function is therefore
|
|
||||||
// a combination of [FirstGraphemeCluster], [FirstWord], [FirstSentence], and
|
|
||||||
// [FirstLineSegment].
|
|
||||||
//
|
|
||||||
// The "boundaries" return value can be evaluated as follows:
|
|
||||||
//
|
|
||||||
// - boundaries&MaskWord != 0: The boundary is a word boundary.
|
|
||||||
// - boundaries&MaskWord == 0: The boundary is not a word boundary.
|
|
||||||
// - boundaries&MaskSentence != 0: The boundary is a sentence boundary.
|
|
||||||
// - boundaries&MaskSentence == 0: The boundary is not a sentence boundary.
|
|
||||||
// - boundaries&MaskLine == LineDontBreak: You must not break the line at the
|
|
||||||
// boundary.
|
|
||||||
// - boundaries&MaskLine == LineMustBreak: You must break the line at the
|
|
||||||
// boundary.
|
|
||||||
// - boundaries&MaskLine == LineCanBreak: You may or may not break the line at
|
|
||||||
// the boundary.
|
|
||||||
// - boundaries >> ShiftWidth: The width of the grapheme cluster for most
|
|
||||||
// monospace fonts where a value of 1 represents one character cell.
|
|
||||||
//
|
|
||||||
// This function can be called continuously to extract all grapheme clusters
|
|
||||||
// from a byte slice, as illustrated in the examples below.
|
|
||||||
//
|
|
||||||
// If you don't know which state to pass, for example when calling the function
|
|
||||||
// for the first time, you must pass -1. For consecutive calls, pass the state
|
|
||||||
// and rest slice returned by the previous call.
|
|
||||||
//
|
|
||||||
// The "rest" slice is the sub-slice of the original byte slice "b" starting
|
|
||||||
// after the last byte of the identified grapheme cluster. If the length of the
|
|
||||||
// "rest" slice is 0, the entire byte slice "b" has been processed. The
|
|
||||||
// "cluster" byte slice is the sub-slice of the input slice containing the
|
|
||||||
// first identified grapheme cluster.
|
|
||||||
//
|
|
||||||
// Given an empty byte slice "b", the function returns nil values.
|
|
||||||
//
|
|
||||||
// While slightly less convenient than using the Graphemes class, this function
|
|
||||||
// has much better performance and makes no allocations. It lends itself well to
|
|
||||||
// large byte slices.
|
|
||||||
//
|
|
||||||
// Note that in accordance with [UAX #14 LB3], the final segment will end with
|
|
||||||
// a mandatory line break (boundaries&MaskLine == LineMustBreak). You can choose
|
|
||||||
// to ignore this by checking if the length of the "rest" slice is 0 and calling
|
|
||||||
// [HasTrailingLineBreak] or [HasTrailingLineBreakInString] on the last rune.
|
|
||||||
//
|
|
||||||
// [UAX #14 LB3]: https://www.unicode.org/reports/tr14/#Algorithm
|
|
||||||
func Step(b []byte, state int) (cluster, rest []byte, boundaries int, newState int) {
|
|
||||||
// An empty byte slice returns nothing.
|
|
||||||
if len(b) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the first rune.
|
|
||||||
r, length := utf8.DecodeRune(b)
|
|
||||||
if len(b) <= length { // If we're already past the end, there is nothing else to parse.
|
|
||||||
var prop int
|
|
||||||
if state < 0 {
|
|
||||||
prop = propertyGraphemes(r)
|
|
||||||
} else {
|
|
||||||
prop = state >> shiftPropState
|
|
||||||
}
|
|
||||||
return b, nil, LineMustBreak | (1 << shiftWord) | (1 << shiftSentence) | (runeWidth(r, prop) << ShiftWidth), grAny | (wbAny << shiftWordState) | (sbAny << shiftSentenceState) | (lbAny << shiftLineState) | (prop << shiftPropState)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we don't know the state, determine it now.
|
|
||||||
var graphemeState, wordState, sentenceState, lineState, firstProp int
|
|
||||||
remainder := b[length:]
|
|
||||||
if state < 0 {
|
|
||||||
graphemeState, firstProp, _ = transitionGraphemeState(state, r)
|
|
||||||
wordState, _ = transitionWordBreakState(state, r, remainder, "")
|
|
||||||
sentenceState, _ = transitionSentenceBreakState(state, r, remainder, "")
|
|
||||||
lineState, _ = transitionLineBreakState(state, r, remainder, "")
|
|
||||||
} else {
|
|
||||||
graphemeState = state & maskGraphemeState
|
|
||||||
wordState = (state >> shiftWordState) & maskWordState
|
|
||||||
sentenceState = (state >> shiftSentenceState) & maskSentenceState
|
|
||||||
lineState = (state >> shiftLineState) & maskLineState
|
|
||||||
firstProp = state >> shiftPropState
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transition until we find a grapheme cluster boundary.
|
|
||||||
width := runeWidth(r, firstProp)
|
|
||||||
for {
|
|
||||||
var (
|
|
||||||
graphemeBoundary, wordBoundary, sentenceBoundary bool
|
|
||||||
lineBreak, prop int
|
|
||||||
)
|
|
||||||
|
|
||||||
r, l := utf8.DecodeRune(remainder)
|
|
||||||
remainder = b[length+l:]
|
|
||||||
|
|
||||||
graphemeState, prop, graphemeBoundary = transitionGraphemeState(graphemeState, r)
|
|
||||||
wordState, wordBoundary = transitionWordBreakState(wordState, r, remainder, "")
|
|
||||||
sentenceState, sentenceBoundary = transitionSentenceBreakState(sentenceState, r, remainder, "")
|
|
||||||
lineState, lineBreak = transitionLineBreakState(lineState, r, remainder, "")
|
|
||||||
|
|
||||||
if graphemeBoundary {
|
|
||||||
boundary := lineBreak | (width << ShiftWidth)
|
|
||||||
if wordBoundary {
|
|
||||||
boundary |= 1 << shiftWord
|
|
||||||
}
|
|
||||||
if sentenceBoundary {
|
|
||||||
boundary |= 1 << shiftSentence
|
|
||||||
}
|
|
||||||
return b[:length], b[length:], boundary, graphemeState | (wordState << shiftWordState) | (sentenceState << shiftSentenceState) | (lineState << shiftLineState) | (prop << shiftPropState)
|
|
||||||
}
|
|
||||||
|
|
||||||
if firstProp == prExtendedPictographic {
|
|
||||||
if r == vs15 {
|
|
||||||
width = 1
|
|
||||||
} else if r == vs16 {
|
|
||||||
width = 2
|
|
||||||
}
|
|
||||||
} else if firstProp != prRegionalIndicator && firstProp != prL {
|
|
||||||
width += runeWidth(r, prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
length += l
|
|
||||||
if len(b) <= length {
|
|
||||||
return b, nil, LineMustBreak | (1 << shiftWord) | (1 << shiftSentence) | (width << ShiftWidth), grAny | (wbAny << shiftWordState) | (sbAny << shiftSentenceState) | (lbAny << shiftLineState) | (prop << shiftPropState)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// StepString is like [Step] but its input and outputs are strings.
|
|
||||||
func StepString(str string, state int) (cluster, rest string, boundaries int, newState int) {
|
|
||||||
// An empty byte slice returns nothing.
|
|
||||||
if len(str) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the first rune.
|
|
||||||
r, length := utf8.DecodeRuneInString(str)
|
|
||||||
if len(str) <= length { // If we're already past the end, there is nothing else to parse.
|
|
||||||
prop := propertyGraphemes(r)
|
|
||||||
return str, "", LineMustBreak | (1 << shiftWord) | (1 << shiftSentence) | (runeWidth(r, prop) << ShiftWidth), grAny | (wbAny << shiftWordState) | (sbAny << shiftSentenceState) | (lbAny << shiftLineState)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we don't know the state, determine it now.
|
|
||||||
var graphemeState, wordState, sentenceState, lineState, firstProp int
|
|
||||||
remainder := str[length:]
|
|
||||||
if state < 0 {
|
|
||||||
graphemeState, firstProp, _ = transitionGraphemeState(state, r)
|
|
||||||
wordState, _ = transitionWordBreakState(state, r, nil, remainder)
|
|
||||||
sentenceState, _ = transitionSentenceBreakState(state, r, nil, remainder)
|
|
||||||
lineState, _ = transitionLineBreakState(state, r, nil, remainder)
|
|
||||||
} else {
|
|
||||||
graphemeState = state & maskGraphemeState
|
|
||||||
wordState = (state >> shiftWordState) & maskWordState
|
|
||||||
sentenceState = (state >> shiftSentenceState) & maskSentenceState
|
|
||||||
lineState = (state >> shiftLineState) & maskLineState
|
|
||||||
firstProp = state >> shiftPropState
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transition until we find a grapheme cluster boundary.
|
|
||||||
width := runeWidth(r, firstProp)
|
|
||||||
for {
|
|
||||||
var (
|
|
||||||
graphemeBoundary, wordBoundary, sentenceBoundary bool
|
|
||||||
lineBreak, prop int
|
|
||||||
)
|
|
||||||
|
|
||||||
r, l := utf8.DecodeRuneInString(remainder)
|
|
||||||
remainder = str[length+l:]
|
|
||||||
|
|
||||||
graphemeState, prop, graphemeBoundary = transitionGraphemeState(graphemeState, r)
|
|
||||||
wordState, wordBoundary = transitionWordBreakState(wordState, r, nil, remainder)
|
|
||||||
sentenceState, sentenceBoundary = transitionSentenceBreakState(sentenceState, r, nil, remainder)
|
|
||||||
lineState, lineBreak = transitionLineBreakState(lineState, r, nil, remainder)
|
|
||||||
|
|
||||||
if graphemeBoundary {
|
|
||||||
boundary := lineBreak | (width << ShiftWidth)
|
|
||||||
if wordBoundary {
|
|
||||||
boundary |= 1 << shiftWord
|
|
||||||
}
|
|
||||||
if sentenceBoundary {
|
|
||||||
boundary |= 1 << shiftSentence
|
|
||||||
}
|
|
||||||
return str[:length], str[length:], boundary, graphemeState | (wordState << shiftWordState) | (sentenceState << shiftSentenceState) | (lineState << shiftLineState) | (prop << shiftPropState)
|
|
||||||
}
|
|
||||||
|
|
||||||
if firstProp == prExtendedPictographic {
|
|
||||||
if r == vs15 {
|
|
||||||
width = 1
|
|
||||||
} else if r == vs16 {
|
|
||||||
width = 2
|
|
||||||
}
|
|
||||||
} else if firstProp != prRegionalIndicator && firstProp != prL {
|
|
||||||
width += runeWidth(r, prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
length += l
|
|
||||||
if len(str) <= length {
|
|
||||||
return str, "", LineMustBreak | (1 << shiftWord) | (1 << shiftSentence) | (width << ShiftWidth), grAny | (wbAny << shiftWordState) | (sbAny << shiftSentenceState) | (lbAny << shiftLineState) | (prop << shiftPropState)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
61
vendor/github.com/rivo/uniseg/width.go
generated
vendored
61
vendor/github.com/rivo/uniseg/width.go
generated
vendored
|
|
@ -1,61 +0,0 @@
|
||||||
package uniseg
|
|
||||||
|
|
||||||
// EastAsianAmbiguousWidth specifies the monospace width for East Asian
|
|
||||||
// characters classified as Ambiguous. The default is 1 but some rare fonts
|
|
||||||
// render them with a width of 2.
|
|
||||||
var EastAsianAmbiguousWidth = 1
|
|
||||||
|
|
||||||
// runeWidth returns the monospace width for the given rune. The provided
|
|
||||||
// grapheme property is a value mapped by the [graphemeCodePoints] table.
|
|
||||||
//
|
|
||||||
// Every rune has a width of 1, except for runes with the following properties
|
|
||||||
// (evaluated in this order):
|
|
||||||
//
|
|
||||||
// - Control, CR, LF, Extend, ZWJ: Width of 0
|
|
||||||
// - \u2e3a, TWO-EM DASH: Width of 3
|
|
||||||
// - \u2e3b, THREE-EM DASH: Width of 4
|
|
||||||
// - East-Asian width Fullwidth and Wide: Width of 2 (Ambiguous and Neutral
|
|
||||||
// have a width of 1)
|
|
||||||
// - Regional Indicator: Width of 2
|
|
||||||
// - Extended Pictographic: Width of 2, unless Emoji Presentation is "No".
|
|
||||||
func runeWidth(r rune, graphemeProperty int) int {
|
|
||||||
switch graphemeProperty {
|
|
||||||
case prControl, prCR, prLF, prExtend, prZWJ:
|
|
||||||
return 0
|
|
||||||
case prRegionalIndicator:
|
|
||||||
return 2
|
|
||||||
case prExtendedPictographic:
|
|
||||||
if property(emojiPresentation, r) == prEmojiPresentation {
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
switch r {
|
|
||||||
case 0x2e3a:
|
|
||||||
return 3
|
|
||||||
case 0x2e3b:
|
|
||||||
return 4
|
|
||||||
}
|
|
||||||
|
|
||||||
switch propertyEastAsianWidth(r) {
|
|
||||||
case prW, prF:
|
|
||||||
return 2
|
|
||||||
case prA:
|
|
||||||
return EastAsianAmbiguousWidth
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringWidth returns the monospace width for the given string, that is, the
|
|
||||||
// number of same-size cells to be occupied by the string.
|
|
||||||
func StringWidth(s string) (width int) {
|
|
||||||
state := -1
|
|
||||||
for len(s) > 0 {
|
|
||||||
var w int
|
|
||||||
_, s, w, state = FirstGraphemeClusterInString(s, state)
|
|
||||||
width += w
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
89
vendor/github.com/rivo/uniseg/word.go
generated
vendored
89
vendor/github.com/rivo/uniseg/word.go
generated
vendored
|
|
@ -1,89 +0,0 @@
|
||||||
package uniseg
|
|
||||||
|
|
||||||
import "unicode/utf8"
|
|
||||||
|
|
||||||
// FirstWord returns the first word found in the given byte slice according to
|
|
||||||
// the rules of [Unicode Standard Annex #29, Word Boundaries]. This function can
|
|
||||||
// be called continuously to extract all words from a byte slice, as illustrated
|
|
||||||
// in the example below.
|
|
||||||
//
|
|
||||||
// If you don't know the current state, for example when calling the function
|
|
||||||
// for the first time, you must pass -1. For consecutive calls, pass the state
|
|
||||||
// and rest slice returned by the previous call.
|
|
||||||
//
|
|
||||||
// The "rest" slice is the sub-slice of the original byte slice "b" starting
|
|
||||||
// after the last byte of the identified word. If the length of the "rest" slice
|
|
||||||
// is 0, the entire byte slice "b" has been processed. The "word" byte slice is
|
|
||||||
// the sub-slice of the input slice containing the identified word.
|
|
||||||
//
|
|
||||||
// Given an empty byte slice "b", the function returns nil values.
|
|
||||||
//
|
|
||||||
// [Unicode Standard Annex #29, Word Boundaries]: http://unicode.org/reports/tr29/#Word_Boundaries
|
|
||||||
func FirstWord(b []byte, state int) (word, rest []byte, newState int) {
|
|
||||||
// An empty byte slice returns nothing.
|
|
||||||
if len(b) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the first rune.
|
|
||||||
r, length := utf8.DecodeRune(b)
|
|
||||||
if len(b) <= length { // If we're already past the end, there is nothing else to parse.
|
|
||||||
return b, nil, wbAny
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we don't know the state, determine it now.
|
|
||||||
if state < 0 {
|
|
||||||
state, _ = transitionWordBreakState(state, r, b[length:], "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transition until we find a boundary.
|
|
||||||
var boundary bool
|
|
||||||
for {
|
|
||||||
r, l := utf8.DecodeRune(b[length:])
|
|
||||||
state, boundary = transitionWordBreakState(state, r, b[length+l:], "")
|
|
||||||
|
|
||||||
if boundary {
|
|
||||||
return b[:length], b[length:], state
|
|
||||||
}
|
|
||||||
|
|
||||||
length += l
|
|
||||||
if len(b) <= length {
|
|
||||||
return b, nil, wbAny
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FirstWordInString is like [FirstWord] but its input and outputs are strings.
|
|
||||||
func FirstWordInString(str string, state int) (word, rest string, newState int) {
|
|
||||||
// An empty byte slice returns nothing.
|
|
||||||
if len(str) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the first rune.
|
|
||||||
r, length := utf8.DecodeRuneInString(str)
|
|
||||||
if len(str) <= length { // If we're already past the end, there is nothing else to parse.
|
|
||||||
return str, "", wbAny
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we don't know the state, determine it now.
|
|
||||||
if state < 0 {
|
|
||||||
state, _ = transitionWordBreakState(state, r, nil, str[length:])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transition until we find a boundary.
|
|
||||||
var boundary bool
|
|
||||||
for {
|
|
||||||
r, l := utf8.DecodeRuneInString(str[length:])
|
|
||||||
state, boundary = transitionWordBreakState(state, r, nil, str[length+l:])
|
|
||||||
|
|
||||||
if boundary {
|
|
||||||
return str[:length], str[length:], state
|
|
||||||
}
|
|
||||||
|
|
||||||
length += l
|
|
||||||
if len(str) <= length {
|
|
||||||
return str, "", wbAny
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1883
vendor/github.com/rivo/uniseg/wordproperties.go
generated
vendored
1883
vendor/github.com/rivo/uniseg/wordproperties.go
generated
vendored
File diff suppressed because it is too large
Load diff
282
vendor/github.com/rivo/uniseg/wordrules.go
generated
vendored
282
vendor/github.com/rivo/uniseg/wordrules.go
generated
vendored
|
|
@ -1,282 +0,0 @@
|
||||||
package uniseg
|
|
||||||
|
|
||||||
import "unicode/utf8"
|
|
||||||
|
|
||||||
// The states of the word break parser.
|
|
||||||
const (
|
|
||||||
wbAny = iota
|
|
||||||
wbCR
|
|
||||||
wbLF
|
|
||||||
wbNewline
|
|
||||||
wbWSegSpace
|
|
||||||
wbHebrewLetter
|
|
||||||
wbALetter
|
|
||||||
wbWB7
|
|
||||||
wbWB7c
|
|
||||||
wbNumeric
|
|
||||||
wbWB11
|
|
||||||
wbKatakana
|
|
||||||
wbExtendNumLet
|
|
||||||
wbOddRI
|
|
||||||
wbEvenRI
|
|
||||||
wbZWJBit = 16 // This bit is set for any states followed by at least one zero-width joiner (see WB4 and WB3c).
|
|
||||||
)
|
|
||||||
|
|
||||||
// wbTransitions implements the word break parser's state transitions. It's
|
|
||||||
// anologous to [grTransitions], see comments there for details.
|
|
||||||
//
|
|
||||||
// Unicode version 15.0.0.
|
|
||||||
func wbTransitions(state, prop int) (newState int, wordBreak bool, rule int) {
|
|
||||||
switch uint64(state) | uint64(prop)<<32 {
|
|
||||||
// WB3b.
|
|
||||||
case wbAny | prNewline<<32:
|
|
||||||
return wbNewline, true, 32
|
|
||||||
case wbAny | prCR<<32:
|
|
||||||
return wbCR, true, 32
|
|
||||||
case wbAny | prLF<<32:
|
|
||||||
return wbLF, true, 32
|
|
||||||
|
|
||||||
// WB3a.
|
|
||||||
case wbNewline | prAny<<32:
|
|
||||||
return wbAny, true, 31
|
|
||||||
case wbCR | prAny<<32:
|
|
||||||
return wbAny, true, 31
|
|
||||||
case wbLF | prAny<<32:
|
|
||||||
return wbAny, true, 31
|
|
||||||
|
|
||||||
// WB3.
|
|
||||||
case wbCR | prLF<<32:
|
|
||||||
return wbLF, false, 30
|
|
||||||
|
|
||||||
// WB3d.
|
|
||||||
case wbAny | prWSegSpace<<32:
|
|
||||||
return wbWSegSpace, true, 9990
|
|
||||||
case wbWSegSpace | prWSegSpace<<32:
|
|
||||||
return wbWSegSpace, false, 34
|
|
||||||
|
|
||||||
// WB5.
|
|
||||||
case wbAny | prALetter<<32:
|
|
||||||
return wbALetter, true, 9990
|
|
||||||
case wbAny | prHebrewLetter<<32:
|
|
||||||
return wbHebrewLetter, true, 9990
|
|
||||||
case wbALetter | prALetter<<32:
|
|
||||||
return wbALetter, false, 50
|
|
||||||
case wbALetter | prHebrewLetter<<32:
|
|
||||||
return wbHebrewLetter, false, 50
|
|
||||||
case wbHebrewLetter | prALetter<<32:
|
|
||||||
return wbALetter, false, 50
|
|
||||||
case wbHebrewLetter | prHebrewLetter<<32:
|
|
||||||
return wbHebrewLetter, false, 50
|
|
||||||
|
|
||||||
// WB7. Transitions to wbWB7 handled by transitionWordBreakState().
|
|
||||||
case wbWB7 | prALetter<<32:
|
|
||||||
return wbALetter, false, 70
|
|
||||||
case wbWB7 | prHebrewLetter<<32:
|
|
||||||
return wbHebrewLetter, false, 70
|
|
||||||
|
|
||||||
// WB7a.
|
|
||||||
case wbHebrewLetter | prSingleQuote<<32:
|
|
||||||
return wbAny, false, 71
|
|
||||||
|
|
||||||
// WB7c. Transitions to wbWB7c handled by transitionWordBreakState().
|
|
||||||
case wbWB7c | prHebrewLetter<<32:
|
|
||||||
return wbHebrewLetter, false, 73
|
|
||||||
|
|
||||||
// WB8.
|
|
||||||
case wbAny | prNumeric<<32:
|
|
||||||
return wbNumeric, true, 9990
|
|
||||||
case wbNumeric | prNumeric<<32:
|
|
||||||
return wbNumeric, false, 80
|
|
||||||
|
|
||||||
// WB9.
|
|
||||||
case wbALetter | prNumeric<<32:
|
|
||||||
return wbNumeric, false, 90
|
|
||||||
case wbHebrewLetter | prNumeric<<32:
|
|
||||||
return wbNumeric, false, 90
|
|
||||||
|
|
||||||
// WB10.
|
|
||||||
case wbNumeric | prALetter<<32:
|
|
||||||
return wbALetter, false, 100
|
|
||||||
case wbNumeric | prHebrewLetter<<32:
|
|
||||||
return wbHebrewLetter, false, 100
|
|
||||||
|
|
||||||
// WB11. Transitions to wbWB11 handled by transitionWordBreakState().
|
|
||||||
case wbWB11 | prNumeric<<32:
|
|
||||||
return wbNumeric, false, 110
|
|
||||||
|
|
||||||
// WB13.
|
|
||||||
case wbAny | prKatakana<<32:
|
|
||||||
return wbKatakana, true, 9990
|
|
||||||
case wbKatakana | prKatakana<<32:
|
|
||||||
return wbKatakana, false, 130
|
|
||||||
|
|
||||||
// WB13a.
|
|
||||||
case wbAny | prExtendNumLet<<32:
|
|
||||||
return wbExtendNumLet, true, 9990
|
|
||||||
case wbALetter | prExtendNumLet<<32:
|
|
||||||
return wbExtendNumLet, false, 131
|
|
||||||
case wbHebrewLetter | prExtendNumLet<<32:
|
|
||||||
return wbExtendNumLet, false, 131
|
|
||||||
case wbNumeric | prExtendNumLet<<32:
|
|
||||||
return wbExtendNumLet, false, 131
|
|
||||||
case wbKatakana | prExtendNumLet<<32:
|
|
||||||
return wbExtendNumLet, false, 131
|
|
||||||
case wbExtendNumLet | prExtendNumLet<<32:
|
|
||||||
return wbExtendNumLet, false, 131
|
|
||||||
|
|
||||||
// WB13b.
|
|
||||||
case wbExtendNumLet | prALetter<<32:
|
|
||||||
return wbALetter, false, 132
|
|
||||||
case wbExtendNumLet | prHebrewLetter<<32:
|
|
||||||
return wbHebrewLetter, false, 132
|
|
||||||
case wbExtendNumLet | prNumeric<<32:
|
|
||||||
return wbNumeric, false, 132
|
|
||||||
case wbExtendNumLet | prKatakana<<32:
|
|
||||||
return wbKatakana, false, 132
|
|
||||||
|
|
||||||
default:
|
|
||||||
return -1, false, -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// transitionWordBreakState determines the new state of the word break parser
|
|
||||||
// given the current state and the next code point. It also returns whether a
|
|
||||||
// word boundary was detected. If more than one code point is needed to
|
|
||||||
// determine the new state, the byte slice or the string starting after rune "r"
|
|
||||||
// can be used (whichever is not nil or empty) for further lookups.
|
|
||||||
func transitionWordBreakState(state int, r rune, b []byte, str string) (newState int, wordBreak bool) {
|
|
||||||
// Determine the property of the next character.
|
|
||||||
nextProperty := property(workBreakCodePoints, r)
|
|
||||||
|
|
||||||
// "Replacing Ignore Rules".
|
|
||||||
if nextProperty == prZWJ {
|
|
||||||
// WB4 (for zero-width joiners).
|
|
||||||
if state == wbNewline || state == wbCR || state == wbLF {
|
|
||||||
return wbAny | wbZWJBit, true // Make sure we don't apply WB4 to WB3a.
|
|
||||||
}
|
|
||||||
if state < 0 {
|
|
||||||
return wbAny | wbZWJBit, false
|
|
||||||
}
|
|
||||||
return state | wbZWJBit, false
|
|
||||||
} else if nextProperty == prExtend || nextProperty == prFormat {
|
|
||||||
// WB4 (for Extend and Format).
|
|
||||||
if state == wbNewline || state == wbCR || state == wbLF {
|
|
||||||
return wbAny, true // Make sure we don't apply WB4 to WB3a.
|
|
||||||
}
|
|
||||||
if state == wbWSegSpace || state == wbAny|wbZWJBit {
|
|
||||||
return wbAny, false // We don't break but this is also not WB3d or WB3c.
|
|
||||||
}
|
|
||||||
if state < 0 {
|
|
||||||
return wbAny, false
|
|
||||||
}
|
|
||||||
return state, false
|
|
||||||
} else if nextProperty == prExtendedPictographic && state >= 0 && state&wbZWJBit != 0 {
|
|
||||||
// WB3c.
|
|
||||||
return wbAny, false
|
|
||||||
}
|
|
||||||
if state >= 0 {
|
|
||||||
state = state &^ wbZWJBit
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the applicable transition in the table.
|
|
||||||
var rule int
|
|
||||||
newState, wordBreak, rule = wbTransitions(state, nextProperty)
|
|
||||||
if newState < 0 {
|
|
||||||
// No specific transition found. Try the less specific ones.
|
|
||||||
anyPropState, anyPropWordBreak, anyPropRule := wbTransitions(state, prAny)
|
|
||||||
anyStateState, anyStateWordBreak, anyStateRule := wbTransitions(wbAny, nextProperty)
|
|
||||||
if anyPropState >= 0 && anyStateState >= 0 {
|
|
||||||
// Both apply. We'll use a mix (see comments for grTransitions).
|
|
||||||
newState, wordBreak, rule = anyStateState, anyStateWordBreak, anyStateRule
|
|
||||||
if anyPropRule < anyStateRule {
|
|
||||||
wordBreak, rule = anyPropWordBreak, anyPropRule
|
|
||||||
}
|
|
||||||
} else if anyPropState >= 0 {
|
|
||||||
// We only have a specific state.
|
|
||||||
newState, wordBreak, rule = anyPropState, anyPropWordBreak, anyPropRule
|
|
||||||
// This branch will probably never be reached because okAnyState will
|
|
||||||
// always be true given the current transition map. But we keep it here
|
|
||||||
// for future modifications to the transition map where this may not be
|
|
||||||
// true anymore.
|
|
||||||
} else if anyStateState >= 0 {
|
|
||||||
// We only have a specific property.
|
|
||||||
newState, wordBreak, rule = anyStateState, anyStateWordBreak, anyStateRule
|
|
||||||
} else {
|
|
||||||
// No known transition. WB999: Any ÷ Any.
|
|
||||||
newState, wordBreak, rule = wbAny, true, 9990
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// For those rules that need to look up runes further in the string, we
|
|
||||||
// determine the property after nextProperty, skipping over Format, Extend,
|
|
||||||
// and ZWJ (according to WB4). It's -1 if not needed, if such a rune cannot
|
|
||||||
// be determined (because the text ends or the rune is faulty).
|
|
||||||
farProperty := -1
|
|
||||||
if rule > 60 &&
|
|
||||||
(state == wbALetter || state == wbHebrewLetter || state == wbNumeric) &&
|
|
||||||
(nextProperty == prMidLetter || nextProperty == prMidNumLet || nextProperty == prSingleQuote || // WB6.
|
|
||||||
nextProperty == prDoubleQuote || // WB7b.
|
|
||||||
nextProperty == prMidNum) { // WB12.
|
|
||||||
for {
|
|
||||||
var (
|
|
||||||
r rune
|
|
||||||
length int
|
|
||||||
)
|
|
||||||
if b != nil { // Byte slice version.
|
|
||||||
r, length = utf8.DecodeRune(b)
|
|
||||||
b = b[length:]
|
|
||||||
} else { // String version.
|
|
||||||
r, length = utf8.DecodeRuneInString(str)
|
|
||||||
str = str[length:]
|
|
||||||
}
|
|
||||||
if r == utf8.RuneError {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
prop := property(workBreakCodePoints, r)
|
|
||||||
if prop == prExtend || prop == prFormat || prop == prZWJ {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
farProperty = prop
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WB6.
|
|
||||||
if rule > 60 &&
|
|
||||||
(state == wbALetter || state == wbHebrewLetter) &&
|
|
||||||
(nextProperty == prMidLetter || nextProperty == prMidNumLet || nextProperty == prSingleQuote) &&
|
|
||||||
(farProperty == prALetter || farProperty == prHebrewLetter) {
|
|
||||||
return wbWB7, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// WB7b.
|
|
||||||
if rule > 72 &&
|
|
||||||
state == wbHebrewLetter &&
|
|
||||||
nextProperty == prDoubleQuote &&
|
|
||||||
farProperty == prHebrewLetter {
|
|
||||||
return wbWB7c, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// WB12.
|
|
||||||
if rule > 120 &&
|
|
||||||
state == wbNumeric &&
|
|
||||||
(nextProperty == prMidNum || nextProperty == prMidNumLet || nextProperty == prSingleQuote) &&
|
|
||||||
farProperty == prNumeric {
|
|
||||||
return wbWB11, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// WB15 and WB16.
|
|
||||||
if newState == wbAny && nextProperty == prRegionalIndicator {
|
|
||||||
if state != wbOddRI && state != wbEvenRI { // Includes state == -1.
|
|
||||||
// Transition into the first RI.
|
|
||||||
return wbOddRI, true
|
|
||||||
}
|
|
||||||
if state == wbOddRI {
|
|
||||||
// Don't break pairs of Regional Indicators.
|
|
||||||
return wbEvenRI, false
|
|
||||||
}
|
|
||||||
return wbOddRI, true // We can break after a pair.
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
498
vendor/go.mau.fi/util/confusable/confusables.go
vendored
498
vendor/go.mau.fi/util/confusable/confusables.go
vendored
File diff suppressed because it is too large
Load diff
40
vendor/go.mau.fi/util/dbutil/log.go
vendored
40
vendor/go.mau.fi/util/dbutil/log.go
vendored
|
|
@ -3,6 +3,7 @@ package dbutil
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -92,12 +93,14 @@ func (z zeroLogger) DoUpgrade(from, to int, message string, txn TxnMode) {
|
||||||
|
|
||||||
var whitespaceRegex = regexp.MustCompile(`\s+`)
|
var whitespaceRegex = regexp.MustCompile(`\s+`)
|
||||||
|
|
||||||
|
var GlobalSafeQueryLog bool
|
||||||
|
|
||||||
func (z zeroLogger) QueryTiming(ctx context.Context, method, query string, args []any, nrows int, duration time.Duration, err error) {
|
func (z zeroLogger) QueryTiming(ctx context.Context, method, query string, args []any, nrows int, duration time.Duration, err error) {
|
||||||
log := zerolog.Ctx(ctx)
|
log := zerolog.Ctx(ctx)
|
||||||
if log.GetLevel() == zerolog.Disabled || log == zerolog.DefaultContextLogger {
|
if log.GetLevel() == zerolog.Disabled || log == zerolog.DefaultContextLogger {
|
||||||
log = z.l
|
log = z.l
|
||||||
}
|
}
|
||||||
if (!z.TraceLogAllQueries || log.GetLevel() != zerolog.TraceLevel) && duration < 1*time.Second {
|
if (!z.TraceLogAllQueries || log.GetLevel() != zerolog.TraceLevel) && !GlobalSafeQueryLog && duration < 1*time.Second {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if nrows > -1 {
|
if nrows > -1 {
|
||||||
|
|
@ -105,13 +108,32 @@ func (z zeroLogger) QueryTiming(ctx context.Context, method, query string, args
|
||||||
log = &rowLog
|
log = &rowLog
|
||||||
}
|
}
|
||||||
query = strings.TrimSpace(whitespaceRegex.ReplaceAllLiteralString(query, " "))
|
query = strings.TrimSpace(whitespaceRegex.ReplaceAllLiteralString(query, " "))
|
||||||
log.Trace().
|
callerSkipFrame := z.CallerSkipFrame
|
||||||
Err(err).
|
if GlobalSafeQueryLog || duration > 1*time.Second {
|
||||||
Int64("duration_µs", duration.Microseconds()).
|
for ; callerSkipFrame < 10; callerSkipFrame++ {
|
||||||
Str("method", method).
|
_, filename, _, _ := runtime.Caller(callerSkipFrame)
|
||||||
Str("query", query).
|
if !strings.Contains(filename, "/dbutil/") {
|
||||||
Interface("query_args", args).
|
break
|
||||||
Msg("Query")
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if GlobalSafeQueryLog {
|
||||||
|
log.Debug().
|
||||||
|
Err(err).
|
||||||
|
Int64("duration_µs", duration.Microseconds()).
|
||||||
|
Str("method", method).
|
||||||
|
Str("query", query).
|
||||||
|
Caller(callerSkipFrame).
|
||||||
|
Msg("Query")
|
||||||
|
} else {
|
||||||
|
log.Trace().
|
||||||
|
Err(err).
|
||||||
|
Int64("duration_µs", duration.Microseconds()).
|
||||||
|
Str("method", method).
|
||||||
|
Str("query", query).
|
||||||
|
Interface("query_args", args).
|
||||||
|
Msg("Query")
|
||||||
|
}
|
||||||
if duration >= 1*time.Second {
|
if duration >= 1*time.Second {
|
||||||
evt := log.Warn().
|
evt := log.Warn().
|
||||||
Float64("duration_seconds", duration.Seconds()).
|
Float64("duration_seconds", duration.Seconds()).
|
||||||
|
|
@ -119,7 +141,7 @@ func (z zeroLogger) QueryTiming(ctx context.Context, method, query string, args
|
||||||
Str("method", method).
|
Str("method", method).
|
||||||
Str("query", query)
|
Str("query", query)
|
||||||
if z.Caller {
|
if z.Caller {
|
||||||
evt = evt.Caller(z.CallerSkipFrame)
|
evt = evt.Caller(callerSkipFrame)
|
||||||
}
|
}
|
||||||
evt.Msg("Query took long")
|
evt.Msg("Query took long")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
12
vendor/go.mau.fi/util/dbutil/transaction.go
vendored
12
vendor/go.mau.fi/util/dbutil/transaction.go
vendored
|
|
@ -120,7 +120,11 @@ func (db *Database) DoTxn(ctx context.Context, opts *TxnOptions, fn func(ctx con
|
||||||
log.Trace().Err(err).Msg("Failed to begin transaction")
|
log.Trace().Err(err).Msg("Failed to begin transaction")
|
||||||
return exerrors.NewDualError(ErrTxnBegin, err)
|
return exerrors.NewDualError(ErrTxnBegin, err)
|
||||||
}
|
}
|
||||||
log.Trace().Msg("Transaction started")
|
logLevel := zerolog.TraceLevel
|
||||||
|
if GlobalSafeQueryLog {
|
||||||
|
logLevel = zerolog.DebugLevel
|
||||||
|
}
|
||||||
|
log.WithLevel(logLevel).Msg("Transaction started")
|
||||||
tx.noTotalLog = true
|
tx.noTotalLog = true
|
||||||
ctx = log.WithContext(ctx)
|
ctx = log.WithContext(ctx)
|
||||||
ctx = context.WithValue(ctx, db.txnCtxKey, tx)
|
ctx = context.WithValue(ctx, db.txnCtxKey, tx)
|
||||||
|
|
@ -131,16 +135,16 @@ func (db *Database) DoTxn(ctx context.Context, opts *TxnOptions, fn func(ctx con
|
||||||
if rollbackErr != nil {
|
if rollbackErr != nil {
|
||||||
log.Warn().Err(rollbackErr).Msg("Rollback after transaction error failed")
|
log.Warn().Err(rollbackErr).Msg("Rollback after transaction error failed")
|
||||||
} else {
|
} else {
|
||||||
log.Trace().Msg("Rollback successful")
|
log.WithLevel(logLevel).Msg("Rollback successful")
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = tx.Commit()
|
err = tx.Commit()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Trace().Err(err).Msg("Commit failed")
|
log.WithLevel(logLevel).Err(err).Msg("Commit failed")
|
||||||
return exerrors.NewDualError(ErrTxnCommit, err)
|
return exerrors.NewDualError(ErrTxnCommit, err)
|
||||||
}
|
}
|
||||||
log.Trace().Msg("Commit successful")
|
log.WithLevel(logLevel).Msg("Commit successful")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
89
vendor/go.mau.fi/util/exmaps/set.go
vendored
Normal file
89
vendor/go.mau.fi/util/exmaps/set.go
vendored
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
// Copyright (c) 2025 Tulir Asokan
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
package exmaps
|
||||||
|
|
||||||
|
import (
|
||||||
|
"iter"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AbstractSet is an interface implemented by [Set] and [exsync.Set]
|
||||||
|
type AbstractSet[T comparable] interface {
|
||||||
|
Add(item T) bool
|
||||||
|
Has(item T) bool
|
||||||
|
Pop(item T) bool
|
||||||
|
Remove(item T)
|
||||||
|
Size() int
|
||||||
|
AsList() []T
|
||||||
|
Iter() iter.Seq[T]
|
||||||
|
}
|
||||||
|
|
||||||
|
type empty = struct{}
|
||||||
|
|
||||||
|
var emptyVal = empty{}
|
||||||
|
|
||||||
|
// Set is a generic set type based on a map. It is not thread-safe, use [exsync.Set] for a thread-safe variant.
|
||||||
|
type Set[T comparable] map[T]empty
|
||||||
|
|
||||||
|
var _ AbstractSet[int] = (Set[int])(nil)
|
||||||
|
|
||||||
|
func NewSetWithItems[T comparable](items []T) Set[T] {
|
||||||
|
s := make(Set[T], len(items))
|
||||||
|
for _, item := range items {
|
||||||
|
s[item] = emptyVal
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Set[T]) Size() int {
|
||||||
|
return len(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Set[T]) Add(item T) bool {
|
||||||
|
_, exists := s[item]
|
||||||
|
if !exists {
|
||||||
|
s[item] = emptyVal
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Set[T]) Remove(item T) {
|
||||||
|
delete(s, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Set[T]) Pop(item T) bool {
|
||||||
|
_, exists := s[item]
|
||||||
|
if exists {
|
||||||
|
delete(s, item)
|
||||||
|
}
|
||||||
|
return exists
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Set[T]) Has(item T) bool {
|
||||||
|
_, exists := s[item]
|
||||||
|
return exists
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Set[T]) AsList() []T {
|
||||||
|
result := make([]T, len(s))
|
||||||
|
i := 0
|
||||||
|
for item := range s {
|
||||||
|
result[i] = item
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Set[T]) Iter() iter.Seq[T] {
|
||||||
|
return func(yield func(T) bool) {
|
||||||
|
for item := range s {
|
||||||
|
if !yield(item) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
38
vendor/go.mau.fi/util/exslices/delete.go
vendored
Normal file
38
vendor/go.mau.fi/util/exslices/delete.go
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
// Copyright (c) 2025 Tulir Asokan
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
package exslices
|
||||||
|
|
||||||
|
import (
|
||||||
|
"slices"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FastDeleteIndex deletes the item at the given index without preserving slice order.
|
||||||
|
// This is faster than normal deletion, as it doesn't need to copy all elements after the deleted index.
|
||||||
|
func FastDeleteIndex[T any](s []T, index int) []T {
|
||||||
|
s[index] = s[len(s)-1]
|
||||||
|
clear(s[len(s)-1:])
|
||||||
|
return s[:len(s)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// FastDeleteItem finds the first index of the given item in the slice and deletes it without preserving slice order.
|
||||||
|
// This is faster than normal deletion, as it doesn't need to copy all elements after the deleted index.
|
||||||
|
func FastDeleteItem[T comparable](s []T, item T) []T {
|
||||||
|
index := slices.Index(s, item)
|
||||||
|
if index < 0 {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return FastDeleteIndex(s, index)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteItem finds the first index of the given item in the slice and deletes it.
|
||||||
|
func DeleteItem[T comparable](s []T, item T) []T {
|
||||||
|
index := slices.Index(s, item)
|
||||||
|
if index < 0 {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return slices.Delete(s, index, index+1)
|
||||||
|
}
|
||||||
80
vendor/go.mau.fi/util/exslices/stack.go
vendored
Normal file
80
vendor/go.mau.fi/util/exslices/stack.go
vendored
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
// Copyright (c) 2024 Tulir Asokan
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
package exslices
|
||||||
|
|
||||||
|
import (
|
||||||
|
"iter"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Stack[T comparable] []T
|
||||||
|
|
||||||
|
func (s *Stack[T]) Push(v ...T) {
|
||||||
|
*s = append(*s, v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Peek returns the top item from the stack without removing it.
|
||||||
|
func (s *Stack[T]) Peek() (v T, ok bool) {
|
||||||
|
return s.PeekN(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PeekN returns the nth item from the top of the stack (1-based).
|
||||||
|
func (s *Stack[T]) PeekN(n int) (v T, ok bool) {
|
||||||
|
if len(*s) < n {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
v = (*s)[len(*s)-n]
|
||||||
|
ok = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pop removes and returns the top item from the stack.
|
||||||
|
func (s *Stack[T]) Pop() (v T, ok bool) {
|
||||||
|
v, ok = s.Peek()
|
||||||
|
if ok {
|
||||||
|
*s = (*s)[:len(*s)-1]
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stack[T]) PeekValue() T {
|
||||||
|
v, _ := s.Peek()
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stack[T]) PopValue() T {
|
||||||
|
v, _ := s.Pop()
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Index returns the highest index of the given value in the stack, or -1 if not found.
|
||||||
|
func (s *Stack[T]) Index(val T) int {
|
||||||
|
for i := len(*s) - 1; i >= 0; i-- {
|
||||||
|
if (*s)[i] == val {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Has returns whether the given value is in the stack.
|
||||||
|
func (s *Stack[T]) Has(val T) bool {
|
||||||
|
return s.Index(val) != -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stack[T]) PopIter() iter.Seq[T] {
|
||||||
|
return func(yield func(T) bool) {
|
||||||
|
for {
|
||||||
|
v, ok := s.Pop()
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !yield(v) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
vendor/go.mau.fi/util/exstrings/stringutil.go
vendored
18
vendor/go.mau.fi/util/exstrings/stringutil.go
vendored
|
|
@ -9,6 +9,7 @@ package exstrings
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"crypto/subtle"
|
"crypto/subtle"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
@ -102,3 +103,20 @@ func LongestSequenceOfFunc(a string, fn func(b rune) int) int {
|
||||||
}
|
}
|
||||||
return maxCount
|
return maxCount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func LongestCommonPrefix(in []string) string {
|
||||||
|
if len(in) == 0 {
|
||||||
|
return ""
|
||||||
|
} else if len(in) == 1 {
|
||||||
|
return in[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
minStr := slices.Min(in)
|
||||||
|
maxStr := slices.Max(in)
|
||||||
|
for i := 0; i < len(minStr) && i < len(maxStr); i++ {
|
||||||
|
if minStr[i] != maxStr[i] {
|
||||||
|
return minStr[:i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return minStr
|
||||||
|
}
|
||||||
|
|
|
||||||
11
vendor/go.mau.fi/util/exsync/syncmap.go
vendored
11
vendor/go.mau.fi/util/exsync/syncmap.go
vendored
|
|
@ -71,6 +71,17 @@ func (sm *Map[Key, Value]) Get(key Key) (value Value, ok bool) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetDefault gets a value in the map, or the given default value if there's no such key.
|
||||||
|
func (sm *Map[Key, Value]) GetDefault(key Key, def Value) Value {
|
||||||
|
sm.lock.RLock()
|
||||||
|
value, ok := sm.data[key]
|
||||||
|
sm.lock.RUnlock()
|
||||||
|
if ok {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
// GetOrSet gets a value in the map if the key already exists, otherwise inserts the given value and returns it.
|
// GetOrSet gets a value in the map if the key already exists, otherwise inserts the given value and returns it.
|
||||||
//
|
//
|
||||||
// The boolean return parameter is true if the key already exists, and false if the given value was inserted.
|
// The boolean return parameter is true if the key already exists, and false if the given value was inserted.
|
||||||
|
|
|
||||||
4
vendor/go.mau.fi/util/exsync/syncset.go
vendored
4
vendor/go.mau.fi/util/exsync/syncset.go
vendored
|
|
@ -9,6 +9,8 @@ package exsync
|
||||||
import (
|
import (
|
||||||
"iter"
|
"iter"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"go.mau.fi/util/exmaps"
|
||||||
)
|
)
|
||||||
|
|
||||||
type empty struct{}
|
type empty struct{}
|
||||||
|
|
@ -21,6 +23,8 @@ type Set[T comparable] struct {
|
||||||
l sync.RWMutex
|
l sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ exmaps.AbstractSet[int] = (*Set[int])(nil)
|
||||||
|
|
||||||
// NewSet constructs a Set with an empty map.
|
// NewSet constructs a Set with an empty map.
|
||||||
func NewSet[T comparable]() *Set[T] {
|
func NewSet[T comparable]() *Set[T] {
|
||||||
return NewSetWithMap[T](make(map[T]empty))
|
return NewSetWithMap[T](make(map[T]empty))
|
||||||
|
|
|
||||||
18
vendor/golang.org/x/crypto/argon2/argon2.go
generated
vendored
18
vendor/golang.org/x/crypto/argon2/argon2.go
generated
vendored
|
|
@ -6,7 +6,7 @@
|
||||||
// Argon2 was selected as the winner of the Password Hashing Competition and can
|
// Argon2 was selected as the winner of the Password Hashing Competition and can
|
||||||
// be used to derive cryptographic keys from passwords.
|
// be used to derive cryptographic keys from passwords.
|
||||||
//
|
//
|
||||||
// For a detailed specification of Argon2 see [1].
|
// For a detailed specification of Argon2 see [argon2-specs.pdf].
|
||||||
//
|
//
|
||||||
// If you aren't sure which function you need, use Argon2id (IDKey) and
|
// If you aren't sure which function you need, use Argon2id (IDKey) and
|
||||||
// the parameter recommendations for your scenario.
|
// the parameter recommendations for your scenario.
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
// It uses data-independent memory access, which is preferred for password
|
// It uses data-independent memory access, which is preferred for password
|
||||||
// hashing and password-based key derivation. Argon2i requires more passes over
|
// hashing and password-based key derivation. Argon2i requires more passes over
|
||||||
// memory than Argon2id to protect from trade-off attacks. The recommended
|
// memory than Argon2id to protect from trade-off attacks. The recommended
|
||||||
// parameters (taken from [2]) for non-interactive operations are time=3 and to
|
// parameters (taken from [RFC 9106 Section 7.3]) for non-interactive operations are time=3 and to
|
||||||
// use the maximum available memory.
|
// use the maximum available memory.
|
||||||
//
|
//
|
||||||
// # Argon2id
|
// # Argon2id
|
||||||
|
|
@ -27,11 +27,11 @@
|
||||||
// half of the first iteration over the memory and data-dependent memory access
|
// half of the first iteration over the memory and data-dependent memory access
|
||||||
// for the rest. Argon2id is side-channel resistant and provides better brute-
|
// for the rest. Argon2id is side-channel resistant and provides better brute-
|
||||||
// force cost savings due to time-memory tradeoffs than Argon2i. The recommended
|
// force cost savings due to time-memory tradeoffs than Argon2i. The recommended
|
||||||
// parameters for non-interactive operations (taken from [2]) are time=1 and to
|
// parameters for non-interactive operations (taken from [RFC 9106 Section 7.3]) are time=1 and to
|
||||||
// use the maximum available memory.
|
// use the maximum available memory.
|
||||||
//
|
//
|
||||||
// [1] https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf
|
// [argon2-specs.pdf]: https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf
|
||||||
// [2] https://tools.ietf.org/html/draft-irtf-cfrg-argon2-03#section-9.3
|
// [RFC 9106 Section 7.3]: https://www.rfc-editor.org/rfc/rfc9106.html#section-7.3
|
||||||
package argon2
|
package argon2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
@ -59,7 +59,7 @@ const (
|
||||||
//
|
//
|
||||||
// key := argon2.Key([]byte("some password"), salt, 3, 32*1024, 4, 32)
|
// key := argon2.Key([]byte("some password"), salt, 3, 32*1024, 4, 32)
|
||||||
//
|
//
|
||||||
// The draft RFC recommends[2] time=3, and memory=32*1024 is a sensible number.
|
// [RFC 9106 Section 7.3] recommends time=3, and memory=32*1024 as a sensible number.
|
||||||
// If using that amount of memory (32 MB) is not possible in some contexts then
|
// If using that amount of memory (32 MB) is not possible in some contexts then
|
||||||
// the time parameter can be increased to compensate.
|
// the time parameter can be increased to compensate.
|
||||||
//
|
//
|
||||||
|
|
@ -69,6 +69,8 @@ const (
|
||||||
// adjusted to the number of available CPUs. The cost parameters should be
|
// adjusted to the number of available CPUs. The cost parameters should be
|
||||||
// increased as memory latency and CPU parallelism increases. Remember to get a
|
// increased as memory latency and CPU parallelism increases. Remember to get a
|
||||||
// good random salt.
|
// good random salt.
|
||||||
|
//
|
||||||
|
// [RFC 9106 Section 7.3]: https://www.rfc-editor.org/rfc/rfc9106.html#section-7.3
|
||||||
func Key(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
|
func Key(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
|
||||||
return deriveKey(argon2i, password, salt, nil, nil, time, memory, threads, keyLen)
|
return deriveKey(argon2i, password, salt, nil, nil, time, memory, threads, keyLen)
|
||||||
}
|
}
|
||||||
|
|
@ -83,7 +85,7 @@ func Key(password, salt []byte, time, memory uint32, threads uint8, keyLen uint3
|
||||||
//
|
//
|
||||||
// key := argon2.IDKey([]byte("some password"), salt, 1, 64*1024, 4, 32)
|
// key := argon2.IDKey([]byte("some password"), salt, 1, 64*1024, 4, 32)
|
||||||
//
|
//
|
||||||
// The draft RFC recommends[2] time=1, and memory=64*1024 is a sensible number.
|
// [RFC 9106 Section 7.3] recommends time=1, and memory=64*1024 as a sensible number.
|
||||||
// If using that amount of memory (64 MB) is not possible in some contexts then
|
// If using that amount of memory (64 MB) is not possible in some contexts then
|
||||||
// the time parameter can be increased to compensate.
|
// the time parameter can be increased to compensate.
|
||||||
//
|
//
|
||||||
|
|
@ -93,6 +95,8 @@ func Key(password, salt []byte, time, memory uint32, threads uint8, keyLen uint3
|
||||||
// adjusted to the numbers of available CPUs. The cost parameters should be
|
// adjusted to the numbers of available CPUs. The cost parameters should be
|
||||||
// increased as memory latency and CPU parallelism increases. Remember to get a
|
// increased as memory latency and CPU parallelism increases. Remember to get a
|
||||||
// good random salt.
|
// good random salt.
|
||||||
|
//
|
||||||
|
// [RFC 9106 Section 7.3]: https://www.rfc-editor.org/rfc/rfc9106.html#section-7.3
|
||||||
func IDKey(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
|
func IDKey(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
|
||||||
return deriveKey(argon2id, password, salt, nil, nil, time, memory, threads, keyLen)
|
return deriveKey(argon2id, password, salt, nil, nil, time, memory, threads, keyLen)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
vendor/golang.org/x/crypto/chacha20/chacha_arm64.s
generated
vendored
2
vendor/golang.org/x/crypto/chacha20/chacha_arm64.s
generated
vendored
|
|
@ -29,7 +29,7 @@ loop:
|
||||||
MOVD $NUM_ROUNDS, R21
|
MOVD $NUM_ROUNDS, R21
|
||||||
VLD1 (R11), [V30.S4, V31.S4]
|
VLD1 (R11), [V30.S4, V31.S4]
|
||||||
|
|
||||||
// load contants
|
// load constants
|
||||||
// VLD4R (R10), [V0.S4, V1.S4, V2.S4, V3.S4]
|
// VLD4R (R10), [V0.S4, V1.S4, V2.S4, V3.S4]
|
||||||
WORD $0x4D60E940
|
WORD $0x4D60E940
|
||||||
|
|
||||||
|
|
|
||||||
1
vendor/golang.org/x/crypto/ssh/keys.go
generated
vendored
1
vendor/golang.org/x/crypto/ssh/keys.go
generated
vendored
|
|
@ -1490,6 +1490,7 @@ type openSSHEncryptedPrivateKey struct {
|
||||||
NumKeys uint32
|
NumKeys uint32
|
||||||
PubKey []byte
|
PubKey []byte
|
||||||
PrivKeyBlock []byte
|
PrivKeyBlock []byte
|
||||||
|
Rest []byte `ssh:"rest"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type openSSHPrivateKey struct {
|
type openSSHPrivateKey struct {
|
||||||
|
|
|
||||||
2
vendor/golang.org/x/crypto/ssh/messages.go
generated
vendored
2
vendor/golang.org/x/crypto/ssh/messages.go
generated
vendored
|
|
@ -792,7 +792,7 @@ func marshalString(to []byte, s []byte) []byte {
|
||||||
return to[len(s):]
|
return to[len(s):]
|
||||||
}
|
}
|
||||||
|
|
||||||
var bigIntType = reflect.TypeOf((*big.Int)(nil))
|
var bigIntType = reflect.TypeFor[*big.Int]()
|
||||||
|
|
||||||
// Decode a packet into its corresponding message.
|
// Decode a packet into its corresponding message.
|
||||||
func decode(packet []byte) (interface{}, error) {
|
func decode(packet []byte) (interface{}, error) {
|
||||||
|
|
|
||||||
8
vendor/golang.org/x/crypto/ssh/ssh_gss.go
generated
vendored
8
vendor/golang.org/x/crypto/ssh/ssh_gss.go
generated
vendored
|
|
@ -106,6 +106,13 @@ func parseGSSAPIPayload(payload []byte) (*userAuthRequestGSSAPI, error) {
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("parse uint32 failed")
|
return nil, errors.New("parse uint32 failed")
|
||||||
}
|
}
|
||||||
|
// Each ASN.1 encoded OID must have a minimum
|
||||||
|
// of 2 bytes; 64 maximum mechanisms is an
|
||||||
|
// arbitrary, but reasonable ceiling.
|
||||||
|
const maxMechs = 64
|
||||||
|
if n > maxMechs || int(n)*2 > len(rest) {
|
||||||
|
return nil, errors.New("invalid mechanism count")
|
||||||
|
}
|
||||||
s := &userAuthRequestGSSAPI{
|
s := &userAuthRequestGSSAPI{
|
||||||
N: n,
|
N: n,
|
||||||
OIDS: make([]asn1.ObjectIdentifier, n),
|
OIDS: make([]asn1.ObjectIdentifier, n),
|
||||||
|
|
@ -122,7 +129,6 @@ func parseGSSAPIPayload(payload []byte) (*userAuthRequestGSSAPI, error) {
|
||||||
if rest, err = asn1.Unmarshal(desiredMech, &s.OIDS[i]); err != nil {
|
if rest, err = asn1.Unmarshal(desiredMech, &s.OIDS[i]); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
4
vendor/golang.org/x/crypto/ssh/streamlocal.go
generated
vendored
4
vendor/golang.org/x/crypto/ssh/streamlocal.go
generated
vendored
|
|
@ -44,7 +44,7 @@ func (c *Client) ListenUnix(socketPath string) (net.Listener, error) {
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("ssh: streamlocal-forward@openssh.com request denied by peer")
|
return nil, errors.New("ssh: streamlocal-forward@openssh.com request denied by peer")
|
||||||
}
|
}
|
||||||
ch := c.forwards.add(&net.UnixAddr{Name: socketPath, Net: "unix"})
|
ch := c.forwards.add("unix", socketPath)
|
||||||
|
|
||||||
return &unixListener{socketPath, c, ch}, nil
|
return &unixListener{socketPath, c, ch}, nil
|
||||||
}
|
}
|
||||||
|
|
@ -96,7 +96,7 @@ func (l *unixListener) Accept() (net.Conn, error) {
|
||||||
// Close closes the listener.
|
// Close closes the listener.
|
||||||
func (l *unixListener) Close() error {
|
func (l *unixListener) Close() error {
|
||||||
// this also closes the listener.
|
// this also closes the listener.
|
||||||
l.conn.forwards.remove(&net.UnixAddr{Name: l.socketPath, Net: "unix"})
|
l.conn.forwards.remove("unix", l.socketPath)
|
||||||
m := streamLocalChannelForwardMsg{
|
m := streamLocalChannelForwardMsg{
|
||||||
l.socketPath,
|
l.socketPath,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
124
vendor/golang.org/x/crypto/ssh/tcpip.go
generated
vendored
124
vendor/golang.org/x/crypto/ssh/tcpip.go
generated
vendored
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
@ -22,14 +23,21 @@ import (
|
||||||
// the returned net.Listener. The listener must be serviced, or the
|
// the returned net.Listener. The listener must be serviced, or the
|
||||||
// SSH connection may hang.
|
// SSH connection may hang.
|
||||||
// N must be "tcp", "tcp4", "tcp6", or "unix".
|
// N must be "tcp", "tcp4", "tcp6", or "unix".
|
||||||
|
//
|
||||||
|
// If the address is a hostname, it is sent to the remote peer as-is, without
|
||||||
|
// being resolved locally, and the Listener Addr method will return a zero IP.
|
||||||
func (c *Client) Listen(n, addr string) (net.Listener, error) {
|
func (c *Client) Listen(n, addr string) (net.Listener, error) {
|
||||||
switch n {
|
switch n {
|
||||||
case "tcp", "tcp4", "tcp6":
|
case "tcp", "tcp4", "tcp6":
|
||||||
laddr, err := net.ResolveTCPAddr(n, addr)
|
host, portStr, err := net.SplitHostPort(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return c.ListenTCP(laddr)
|
port, err := strconv.ParseInt(portStr, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return c.listenTCPInternal(host, int(port))
|
||||||
case "unix":
|
case "unix":
|
||||||
return c.ListenUnix(addr)
|
return c.ListenUnix(addr)
|
||||||
default:
|
default:
|
||||||
|
|
@ -102,15 +110,24 @@ func (c *Client) handleForwards() {
|
||||||
// ListenTCP requests the remote peer open a listening socket
|
// ListenTCP requests the remote peer open a listening socket
|
||||||
// on laddr. Incoming connections will be available by calling
|
// on laddr. Incoming connections will be available by calling
|
||||||
// Accept on the returned net.Listener.
|
// Accept on the returned net.Listener.
|
||||||
|
//
|
||||||
|
// ListenTCP accepts an IP address, to provide a hostname use [Client.Listen]
|
||||||
|
// with "tcp", "tcp4", or "tcp6" network instead.
|
||||||
func (c *Client) ListenTCP(laddr *net.TCPAddr) (net.Listener, error) {
|
func (c *Client) ListenTCP(laddr *net.TCPAddr) (net.Listener, error) {
|
||||||
c.handleForwardsOnce.Do(c.handleForwards)
|
c.handleForwardsOnce.Do(c.handleForwards)
|
||||||
if laddr.Port == 0 && isBrokenOpenSSHVersion(string(c.ServerVersion())) {
|
if laddr.Port == 0 && isBrokenOpenSSHVersion(string(c.ServerVersion())) {
|
||||||
return c.autoPortListenWorkaround(laddr)
|
return c.autoPortListenWorkaround(laddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return c.listenTCPInternal(laddr.IP.String(), laddr.Port)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) listenTCPInternal(host string, port int) (net.Listener, error) {
|
||||||
|
c.handleForwardsOnce.Do(c.handleForwards)
|
||||||
|
|
||||||
m := channelForwardMsg{
|
m := channelForwardMsg{
|
||||||
laddr.IP.String(),
|
host,
|
||||||
uint32(laddr.Port),
|
uint32(port),
|
||||||
}
|
}
|
||||||
// send message
|
// send message
|
||||||
ok, resp, err := c.SendRequest("tcpip-forward", true, Marshal(&m))
|
ok, resp, err := c.SendRequest("tcpip-forward", true, Marshal(&m))
|
||||||
|
|
@ -123,20 +140,33 @@ func (c *Client) ListenTCP(laddr *net.TCPAddr) (net.Listener, error) {
|
||||||
|
|
||||||
// If the original port was 0, then the remote side will
|
// If the original port was 0, then the remote side will
|
||||||
// supply a real port number in the response.
|
// supply a real port number in the response.
|
||||||
if laddr.Port == 0 {
|
if port == 0 {
|
||||||
var p struct {
|
var p struct {
|
||||||
Port uint32
|
Port uint32
|
||||||
}
|
}
|
||||||
if err := Unmarshal(resp, &p); err != nil {
|
if err := Unmarshal(resp, &p); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
laddr.Port = int(p.Port)
|
port = int(p.Port)
|
||||||
}
|
}
|
||||||
|
// Construct a local address placeholder for the remote listener. If the
|
||||||
|
// original host is an IP address, preserve it so that Listener.Addr()
|
||||||
|
// reports the same IP. If the host is a hostname or cannot be parsed as an
|
||||||
|
// IP, fall back to IPv4zero. The port field is always set, even if the
|
||||||
|
// original port was 0, because in that case the remote server will assign
|
||||||
|
// one, allowing callers to determine which port was selected.
|
||||||
|
ip := net.IPv4zero
|
||||||
|
if parsed, err := netip.ParseAddr(host); err == nil {
|
||||||
|
ip = net.IP(parsed.AsSlice())
|
||||||
|
}
|
||||||
|
laddr := &net.TCPAddr{
|
||||||
|
IP: ip,
|
||||||
|
Port: port,
|
||||||
|
}
|
||||||
|
addr := net.JoinHostPort(host, strconv.FormatInt(int64(port), 10))
|
||||||
|
ch := c.forwards.add("tcp", addr)
|
||||||
|
|
||||||
// Register this forward, using the port number we obtained.
|
return &tcpListener{laddr, addr, c, ch}, nil
|
||||||
ch := c.forwards.add(laddr)
|
|
||||||
|
|
||||||
return &tcpListener{laddr, c, ch}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// forwardList stores a mapping between remote
|
// forwardList stores a mapping between remote
|
||||||
|
|
@ -149,8 +179,9 @@ type forwardList struct {
|
||||||
// forwardEntry represents an established mapping of a laddr on a
|
// forwardEntry represents an established mapping of a laddr on a
|
||||||
// remote ssh server to a channel connected to a tcpListener.
|
// remote ssh server to a channel connected to a tcpListener.
|
||||||
type forwardEntry struct {
|
type forwardEntry struct {
|
||||||
laddr net.Addr
|
addr string // host:port or socket path
|
||||||
c chan forward
|
network string // tcp or unix
|
||||||
|
c chan forward
|
||||||
}
|
}
|
||||||
|
|
||||||
// forward represents an incoming forwarded tcpip connection. The
|
// forward represents an incoming forwarded tcpip connection. The
|
||||||
|
|
@ -161,12 +192,13 @@ type forward struct {
|
||||||
raddr net.Addr // the raddr of the incoming connection
|
raddr net.Addr // the raddr of the incoming connection
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *forwardList) add(addr net.Addr) chan forward {
|
func (l *forwardList) add(n, addr string) chan forward {
|
||||||
l.Lock()
|
l.Lock()
|
||||||
defer l.Unlock()
|
defer l.Unlock()
|
||||||
f := forwardEntry{
|
f := forwardEntry{
|
||||||
laddr: addr,
|
addr: addr,
|
||||||
c: make(chan forward, 1),
|
network: n,
|
||||||
|
c: make(chan forward, 1),
|
||||||
}
|
}
|
||||||
l.entries = append(l.entries, f)
|
l.entries = append(l.entries, f)
|
||||||
return f.c
|
return f.c
|
||||||
|
|
@ -185,19 +217,20 @@ func parseTCPAddr(addr string, port uint32) (*net.TCPAddr, error) {
|
||||||
if port == 0 || port > 65535 {
|
if port == 0 || port > 65535 {
|
||||||
return nil, fmt.Errorf("ssh: port number out of range: %d", port)
|
return nil, fmt.Errorf("ssh: port number out of range: %d", port)
|
||||||
}
|
}
|
||||||
ip := net.ParseIP(string(addr))
|
ip, err := netip.ParseAddr(addr)
|
||||||
if ip == nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("ssh: cannot parse IP address %q", addr)
|
return nil, fmt.Errorf("ssh: cannot parse IP address %q", addr)
|
||||||
}
|
}
|
||||||
return &net.TCPAddr{IP: ip, Port: int(port)}, nil
|
return &net.TCPAddr{IP: net.IP(ip.AsSlice()), Port: int(port)}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *forwardList) handleChannels(in <-chan NewChannel) {
|
func (l *forwardList) handleChannels(in <-chan NewChannel) {
|
||||||
for ch := range in {
|
for ch := range in {
|
||||||
var (
|
var (
|
||||||
laddr net.Addr
|
addr string
|
||||||
raddr net.Addr
|
network string
|
||||||
err error
|
raddr net.Addr
|
||||||
|
err error
|
||||||
)
|
)
|
||||||
switch channelType := ch.ChannelType(); channelType {
|
switch channelType := ch.ChannelType(); channelType {
|
||||||
case "forwarded-tcpip":
|
case "forwarded-tcpip":
|
||||||
|
|
@ -207,40 +240,34 @@ func (l *forwardList) handleChannels(in <-chan NewChannel) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// RFC 4254 section 7.2 specifies that incoming
|
// RFC 4254 section 7.2 specifies that incoming addresses should
|
||||||
// addresses should list the address, in string
|
// list the address that was connected, in string format. It is the
|
||||||
// format. It is implied that this should be an IP
|
// same address used in the tcpip-forward request. The originator
|
||||||
// address, as it would be impossible to connect to it
|
// address is an IP address instead.
|
||||||
// otherwise.
|
addr = net.JoinHostPort(payload.Addr, strconv.FormatUint(uint64(payload.Port), 10))
|
||||||
laddr, err = parseTCPAddr(payload.Addr, payload.Port)
|
|
||||||
if err != nil {
|
|
||||||
ch.Reject(ConnectionFailed, err.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
raddr, err = parseTCPAddr(payload.OriginAddr, payload.OriginPort)
|
raddr, err = parseTCPAddr(payload.OriginAddr, payload.OriginPort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ch.Reject(ConnectionFailed, err.Error())
|
ch.Reject(ConnectionFailed, err.Error())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
network = "tcp"
|
||||||
case "forwarded-streamlocal@openssh.com":
|
case "forwarded-streamlocal@openssh.com":
|
||||||
var payload forwardedStreamLocalPayload
|
var payload forwardedStreamLocalPayload
|
||||||
if err = Unmarshal(ch.ExtraData(), &payload); err != nil {
|
if err = Unmarshal(ch.ExtraData(), &payload); err != nil {
|
||||||
ch.Reject(ConnectionFailed, "could not parse forwarded-streamlocal@openssh.com payload: "+err.Error())
|
ch.Reject(ConnectionFailed, "could not parse forwarded-streamlocal@openssh.com payload: "+err.Error())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
laddr = &net.UnixAddr{
|
addr = payload.SocketPath
|
||||||
Name: payload.SocketPath,
|
|
||||||
Net: "unix",
|
|
||||||
}
|
|
||||||
raddr = &net.UnixAddr{
|
raddr = &net.UnixAddr{
|
||||||
Name: "@",
|
Name: "@",
|
||||||
Net: "unix",
|
Net: "unix",
|
||||||
}
|
}
|
||||||
|
network = "unix"
|
||||||
default:
|
default:
|
||||||
panic(fmt.Errorf("ssh: unknown channel type %s", channelType))
|
panic(fmt.Errorf("ssh: unknown channel type %s", channelType))
|
||||||
}
|
}
|
||||||
if ok := l.forward(laddr, raddr, ch); !ok {
|
if ok := l.forward(network, addr, raddr, ch); !ok {
|
||||||
// Section 7.2, implementations MUST reject spurious incoming
|
// Section 7.2, implementations MUST reject spurious incoming
|
||||||
// connections.
|
// connections.
|
||||||
ch.Reject(Prohibited, "no forward for address")
|
ch.Reject(Prohibited, "no forward for address")
|
||||||
|
|
@ -252,11 +279,11 @@ func (l *forwardList) handleChannels(in <-chan NewChannel) {
|
||||||
|
|
||||||
// remove removes the forward entry, and the channel feeding its
|
// remove removes the forward entry, and the channel feeding its
|
||||||
// listener.
|
// listener.
|
||||||
func (l *forwardList) remove(addr net.Addr) {
|
func (l *forwardList) remove(n, addr string) {
|
||||||
l.Lock()
|
l.Lock()
|
||||||
defer l.Unlock()
|
defer l.Unlock()
|
||||||
for i, f := range l.entries {
|
for i, f := range l.entries {
|
||||||
if addr.Network() == f.laddr.Network() && addr.String() == f.laddr.String() {
|
if n == f.network && addr == f.addr {
|
||||||
l.entries = append(l.entries[:i], l.entries[i+1:]...)
|
l.entries = append(l.entries[:i], l.entries[i+1:]...)
|
||||||
close(f.c)
|
close(f.c)
|
||||||
return
|
return
|
||||||
|
|
@ -274,11 +301,11 @@ func (l *forwardList) closeAll() {
|
||||||
l.entries = nil
|
l.entries = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *forwardList) forward(laddr, raddr net.Addr, ch NewChannel) bool {
|
func (l *forwardList) forward(n, addr string, raddr net.Addr, ch NewChannel) bool {
|
||||||
l.Lock()
|
l.Lock()
|
||||||
defer l.Unlock()
|
defer l.Unlock()
|
||||||
for _, f := range l.entries {
|
for _, f := range l.entries {
|
||||||
if laddr.Network() == f.laddr.Network() && laddr.String() == f.laddr.String() {
|
if n == f.network && addr == f.addr {
|
||||||
f.c <- forward{newCh: ch, raddr: raddr}
|
f.c <- forward{newCh: ch, raddr: raddr}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
@ -288,6 +315,7 @@ func (l *forwardList) forward(laddr, raddr net.Addr, ch NewChannel) bool {
|
||||||
|
|
||||||
type tcpListener struct {
|
type tcpListener struct {
|
||||||
laddr *net.TCPAddr
|
laddr *net.TCPAddr
|
||||||
|
addr string
|
||||||
|
|
||||||
conn *Client
|
conn *Client
|
||||||
in <-chan forward
|
in <-chan forward
|
||||||
|
|
@ -314,13 +342,21 @@ func (l *tcpListener) Accept() (net.Conn, error) {
|
||||||
|
|
||||||
// Close closes the listener.
|
// Close closes the listener.
|
||||||
func (l *tcpListener) Close() error {
|
func (l *tcpListener) Close() error {
|
||||||
|
host, port, err := net.SplitHostPort(l.addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rport, err := strconv.ParseUint(port, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
m := channelForwardMsg{
|
m := channelForwardMsg{
|
||||||
l.laddr.IP.String(),
|
host,
|
||||||
uint32(l.laddr.Port),
|
uint32(rport),
|
||||||
}
|
}
|
||||||
|
|
||||||
// this also closes the listener.
|
// this also closes the listener.
|
||||||
l.conn.forwards.remove(l.laddr)
|
l.conn.forwards.remove("tcp", l.addr)
|
||||||
ok, _, err := l.conn.SendRequest("cancel-tcpip-forward", true, Marshal(&m))
|
ok, _, err := l.conn.SendRequest("cancel-tcpip-forward", true, Marshal(&m))
|
||||||
if err == nil && !ok {
|
if err == nil && !ok {
|
||||||
err = errors.New("ssh: cancel-tcpip-forward failed")
|
err = errors.New("ssh: cancel-tcpip-forward failed")
|
||||||
|
|
|
||||||
76
vendor/golang.org/x/net/http2/frame.go
generated
vendored
76
vendor/golang.org/x/net/http2/frame.go
generated
vendored
|
|
@ -280,6 +280,8 @@ type Framer struct {
|
||||||
// lastHeaderStream is non-zero if the last frame was an
|
// lastHeaderStream is non-zero if the last frame was an
|
||||||
// unfinished HEADERS/CONTINUATION.
|
// unfinished HEADERS/CONTINUATION.
|
||||||
lastHeaderStream uint32
|
lastHeaderStream uint32
|
||||||
|
// lastFrameType holds the type of the last frame for verifying frame order.
|
||||||
|
lastFrameType FrameType
|
||||||
|
|
||||||
maxReadSize uint32
|
maxReadSize uint32
|
||||||
headerBuf [frameHeaderLen]byte
|
headerBuf [frameHeaderLen]byte
|
||||||
|
|
@ -488,30 +490,41 @@ func terminalReadFrameError(err error) bool {
|
||||||
return err != nil
|
return err != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadFrame reads a single frame. The returned Frame is only valid
|
// ReadFrameHeader reads the header of the next frame.
|
||||||
// until the next call to ReadFrame.
|
// It reads the 9-byte fixed frame header, and does not read any portion of the
|
||||||
|
// frame payload. The caller is responsible for consuming the payload, either
|
||||||
|
// with ReadFrameForHeader or directly from the Framer's io.Reader.
|
||||||
//
|
//
|
||||||
// If the frame is larger than previously set with SetMaxReadFrameSize, the
|
// If the frame is larger than previously set with SetMaxReadFrameSize, it
|
||||||
// returned error is ErrFrameTooLarge. Other errors may be of type
|
// returns the frame header and ErrFrameTooLarge.
|
||||||
// ConnectionError, StreamError, or anything else from the underlying
|
|
||||||
// reader.
|
|
||||||
//
|
//
|
||||||
// If ReadFrame returns an error and a non-nil Frame, the Frame's StreamID
|
// If the returned FrameHeader.StreamID is non-zero, it indicates the stream
|
||||||
// indicates the stream responsible for the error.
|
// responsible for the error.
|
||||||
func (fr *Framer) ReadFrame() (Frame, error) {
|
func (fr *Framer) ReadFrameHeader() (FrameHeader, error) {
|
||||||
fr.errDetail = nil
|
fr.errDetail = nil
|
||||||
if fr.lastFrame != nil {
|
|
||||||
fr.lastFrame.invalidate()
|
|
||||||
}
|
|
||||||
fh, err := readFrameHeader(fr.headerBuf[:], fr.r)
|
fh, err := readFrameHeader(fr.headerBuf[:], fr.r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return fh, err
|
||||||
}
|
}
|
||||||
if fh.Length > fr.maxReadSize {
|
if fh.Length > fr.maxReadSize {
|
||||||
if fh == invalidHTTP1LookingFrameHeader() {
|
if fh == invalidHTTP1LookingFrameHeader() {
|
||||||
return nil, fmt.Errorf("http2: failed reading the frame payload: %w, note that the frame header looked like an HTTP/1.1 header", ErrFrameTooLarge)
|
return fh, fmt.Errorf("http2: failed reading the frame payload: %w, note that the frame header looked like an HTTP/1.1 header", ErrFrameTooLarge)
|
||||||
}
|
}
|
||||||
return nil, ErrFrameTooLarge
|
return fh, ErrFrameTooLarge
|
||||||
|
}
|
||||||
|
if err := fr.checkFrameOrder(fh); err != nil {
|
||||||
|
return fh, err
|
||||||
|
}
|
||||||
|
return fh, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadFrameForHeader reads the payload for the frame with the given FrameHeader.
|
||||||
|
//
|
||||||
|
// It behaves identically to ReadFrame, other than not checking the maximum
|
||||||
|
// frame size.
|
||||||
|
func (fr *Framer) ReadFrameForHeader(fh FrameHeader) (Frame, error) {
|
||||||
|
if fr.lastFrame != nil {
|
||||||
|
fr.lastFrame.invalidate()
|
||||||
}
|
}
|
||||||
payload := fr.getReadBuf(fh.Length)
|
payload := fr.getReadBuf(fh.Length)
|
||||||
if _, err := io.ReadFull(fr.r, payload); err != nil {
|
if _, err := io.ReadFull(fr.r, payload); err != nil {
|
||||||
|
|
@ -527,9 +540,7 @@ func (fr *Framer) ReadFrame() (Frame, error) {
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := fr.checkFrameOrder(f); err != nil {
|
fr.lastFrame = f
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if fr.logReads {
|
if fr.logReads {
|
||||||
fr.debugReadLoggerf("http2: Framer %p: read %v", fr, summarizeFrame(f))
|
fr.debugReadLoggerf("http2: Framer %p: read %v", fr, summarizeFrame(f))
|
||||||
}
|
}
|
||||||
|
|
@ -539,6 +550,24 @@ func (fr *Framer) ReadFrame() (Frame, error) {
|
||||||
return f, nil
|
return f, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadFrame reads a single frame. The returned Frame is only valid
|
||||||
|
// until the next call to ReadFrame or ReadFrameBodyForHeader.
|
||||||
|
//
|
||||||
|
// If the frame is larger than previously set with SetMaxReadFrameSize, the
|
||||||
|
// returned error is ErrFrameTooLarge. Other errors may be of type
|
||||||
|
// ConnectionError, StreamError, or anything else from the underlying
|
||||||
|
// reader.
|
||||||
|
//
|
||||||
|
// If ReadFrame returns an error and a non-nil Frame, the Frame's StreamID
|
||||||
|
// indicates the stream responsible for the error.
|
||||||
|
func (fr *Framer) ReadFrame() (Frame, error) {
|
||||||
|
fh, err := fr.ReadFrameHeader()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return fr.ReadFrameForHeader(fh)
|
||||||
|
}
|
||||||
|
|
||||||
// connError returns ConnectionError(code) but first
|
// connError returns ConnectionError(code) but first
|
||||||
// stashes away a public reason to the caller can optionally relay it
|
// stashes away a public reason to the caller can optionally relay it
|
||||||
// to the peer before hanging up on them. This might help others debug
|
// to the peer before hanging up on them. This might help others debug
|
||||||
|
|
@ -551,20 +580,19 @@ func (fr *Framer) connError(code ErrCode, reason string) error {
|
||||||
// checkFrameOrder reports an error if f is an invalid frame to return
|
// checkFrameOrder reports an error if f is an invalid frame to return
|
||||||
// next from ReadFrame. Mostly it checks whether HEADERS and
|
// next from ReadFrame. Mostly it checks whether HEADERS and
|
||||||
// CONTINUATION frames are contiguous.
|
// CONTINUATION frames are contiguous.
|
||||||
func (fr *Framer) checkFrameOrder(f Frame) error {
|
func (fr *Framer) checkFrameOrder(fh FrameHeader) error {
|
||||||
last := fr.lastFrame
|
lastType := fr.lastFrameType
|
||||||
fr.lastFrame = f
|
fr.lastFrameType = fh.Type
|
||||||
if fr.AllowIllegalReads {
|
if fr.AllowIllegalReads {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
fh := f.Header()
|
|
||||||
if fr.lastHeaderStream != 0 {
|
if fr.lastHeaderStream != 0 {
|
||||||
if fh.Type != FrameContinuation {
|
if fh.Type != FrameContinuation {
|
||||||
return fr.connError(ErrCodeProtocol,
|
return fr.connError(ErrCodeProtocol,
|
||||||
fmt.Sprintf("got %s for stream %d; expected CONTINUATION following %s for stream %d",
|
fmt.Sprintf("got %s for stream %d; expected CONTINUATION following %s for stream %d",
|
||||||
fh.Type, fh.StreamID,
|
fh.Type, fh.StreamID,
|
||||||
last.Header().Type, fr.lastHeaderStream))
|
lastType, fr.lastHeaderStream))
|
||||||
}
|
}
|
||||||
if fh.StreamID != fr.lastHeaderStream {
|
if fh.StreamID != fr.lastHeaderStream {
|
||||||
return fr.connError(ErrCodeProtocol,
|
return fr.connError(ErrCodeProtocol,
|
||||||
|
|
@ -1161,7 +1189,7 @@ var defaultRFC9218Priority = PriorityParam{
|
||||||
// PriorityParam struct below is a superset of both schemes. The exported
|
// PriorityParam struct below is a superset of both schemes. The exported
|
||||||
// symbols are from RFC 7540 and the non-exported ones are from RFC 9218.
|
// symbols are from RFC 7540 and the non-exported ones are from RFC 9218.
|
||||||
|
|
||||||
// PriorityParam are the stream prioritzation parameters.
|
// PriorityParam are the stream prioritization parameters.
|
||||||
type PriorityParam struct {
|
type PriorityParam struct {
|
||||||
// StreamDep is a 31-bit stream identifier for the
|
// StreamDep is a 31-bit stream identifier for the
|
||||||
// stream that this stream depends on. Zero means no
|
// stream that this stream depends on. Zero means no
|
||||||
|
|
|
||||||
264
vendor/golang.org/x/net/http2/transport.go
generated
vendored
264
vendor/golang.org/x/net/http2/transport.go
generated
vendored
|
|
@ -9,6 +9,7 @@ package http2
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"compress/flate"
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
"context"
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
|
@ -375,11 +376,24 @@ type ClientConn struct {
|
||||||
// completely unresponsive connection.
|
// completely unresponsive connection.
|
||||||
pendingResets int
|
pendingResets int
|
||||||
|
|
||||||
|
// readBeforeStreamID is the smallest stream ID that has not been followed by
|
||||||
|
// a frame read from the peer. We use this to determine when a request may
|
||||||
|
// have been sent to a completely unresponsive connection:
|
||||||
|
// If the request ID is less than readBeforeStreamID, then we have had some
|
||||||
|
// indication of life on the connection since sending the request.
|
||||||
|
readBeforeStreamID uint32
|
||||||
|
|
||||||
// reqHeaderMu is a 1-element semaphore channel controlling access to sending new requests.
|
// reqHeaderMu is a 1-element semaphore channel controlling access to sending new requests.
|
||||||
// Write to reqHeaderMu to lock it, read from it to unlock.
|
// Write to reqHeaderMu to lock it, read from it to unlock.
|
||||||
// Lock reqmu BEFORE mu or wmu.
|
// Lock reqmu BEFORE mu or wmu.
|
||||||
reqHeaderMu chan struct{}
|
reqHeaderMu chan struct{}
|
||||||
|
|
||||||
|
// internalStateHook reports state changes back to the net/http.ClientConn.
|
||||||
|
// Note that this is different from the user state hook registered by
|
||||||
|
// net/http.ClientConn.SetStateHook: The internal hook calls ClientConn,
|
||||||
|
// which calls the user hook.
|
||||||
|
internalStateHook func()
|
||||||
|
|
||||||
// wmu is held while writing.
|
// wmu is held while writing.
|
||||||
// Acquire BEFORE mu when holding both, to avoid blocking mu on network writes.
|
// Acquire BEFORE mu when holding both, to avoid blocking mu on network writes.
|
||||||
// Only acquire both at the same time when changing peer settings.
|
// Only acquire both at the same time when changing peer settings.
|
||||||
|
|
@ -709,7 +723,7 @@ func canRetryError(err error) bool {
|
||||||
|
|
||||||
func (t *Transport) dialClientConn(ctx context.Context, addr string, singleUse bool) (*ClientConn, error) {
|
func (t *Transport) dialClientConn(ctx context.Context, addr string, singleUse bool) (*ClientConn, error) {
|
||||||
if t.transportTestHooks != nil {
|
if t.transportTestHooks != nil {
|
||||||
return t.newClientConn(nil, singleUse)
|
return t.newClientConn(nil, singleUse, nil)
|
||||||
}
|
}
|
||||||
host, _, err := net.SplitHostPort(addr)
|
host, _, err := net.SplitHostPort(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -719,7 +733,7 @@ func (t *Transport) dialClientConn(ctx context.Context, addr string, singleUse b
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return t.newClientConn(tconn, singleUse)
|
return t.newClientConn(tconn, singleUse, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transport) newTLSConfig(host string) *tls.Config {
|
func (t *Transport) newTLSConfig(host string) *tls.Config {
|
||||||
|
|
@ -771,10 +785,10 @@ func (t *Transport) expectContinueTimeout() time.Duration {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) {
|
func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) {
|
||||||
return t.newClientConn(c, t.disableKeepAlives())
|
return t.newClientConn(c, t.disableKeepAlives(), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, error) {
|
func (t *Transport) newClientConn(c net.Conn, singleUse bool, internalStateHook func()) (*ClientConn, error) {
|
||||||
conf := configFromTransport(t)
|
conf := configFromTransport(t)
|
||||||
cc := &ClientConn{
|
cc := &ClientConn{
|
||||||
t: t,
|
t: t,
|
||||||
|
|
@ -796,6 +810,7 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
|
||||||
pings: make(map[[8]byte]chan struct{}),
|
pings: make(map[[8]byte]chan struct{}),
|
||||||
reqHeaderMu: make(chan struct{}, 1),
|
reqHeaderMu: make(chan struct{}, 1),
|
||||||
lastActive: time.Now(),
|
lastActive: time.Now(),
|
||||||
|
internalStateHook: internalStateHook,
|
||||||
}
|
}
|
||||||
if t.transportTestHooks != nil {
|
if t.transportTestHooks != nil {
|
||||||
t.transportTestHooks.newclientconn(cc)
|
t.transportTestHooks.newclientconn(cc)
|
||||||
|
|
@ -1036,10 +1051,7 @@ func (cc *ClientConn) idleStateLocked() (st clientConnIdleState) {
|
||||||
maxConcurrentOkay = cc.currentRequestCountLocked() < int(cc.maxConcurrentStreams)
|
maxConcurrentOkay = cc.currentRequestCountLocked() < int(cc.maxConcurrentStreams)
|
||||||
}
|
}
|
||||||
|
|
||||||
st.canTakeNewRequest = cc.goAway == nil && !cc.closed && !cc.closing && maxConcurrentOkay &&
|
st.canTakeNewRequest = maxConcurrentOkay && cc.isUsableLocked()
|
||||||
!cc.doNotReuse &&
|
|
||||||
int64(cc.nextStreamID)+2*int64(cc.pendingRequests) < math.MaxInt32 &&
|
|
||||||
!cc.tooIdleLocked()
|
|
||||||
|
|
||||||
// If this connection has never been used for a request and is closed,
|
// If this connection has never been used for a request and is closed,
|
||||||
// then let it take a request (which will fail).
|
// then let it take a request (which will fail).
|
||||||
|
|
@ -1055,6 +1067,31 @@ func (cc *ClientConn) idleStateLocked() (st clientConnIdleState) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cc *ClientConn) isUsableLocked() bool {
|
||||||
|
return cc.goAway == nil &&
|
||||||
|
!cc.closed &&
|
||||||
|
!cc.closing &&
|
||||||
|
!cc.doNotReuse &&
|
||||||
|
int64(cc.nextStreamID)+2*int64(cc.pendingRequests) < math.MaxInt32 &&
|
||||||
|
!cc.tooIdleLocked()
|
||||||
|
}
|
||||||
|
|
||||||
|
// canReserveLocked reports whether a net/http.ClientConn can reserve a slot on this conn.
|
||||||
|
//
|
||||||
|
// This follows slightly different rules than clientConnIdleState.canTakeNewRequest.
|
||||||
|
// We only permit reservations up to the conn's concurrency limit.
|
||||||
|
// This differs from ClientConn.ReserveNewRequest, which permits reservations
|
||||||
|
// past the limit when StrictMaxConcurrentStreams is set.
|
||||||
|
func (cc *ClientConn) canReserveLocked() bool {
|
||||||
|
if cc.currentRequestCountLocked() >= int(cc.maxConcurrentStreams) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !cc.isUsableLocked() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// currentRequestCountLocked reports the number of concurrency slots currently in use,
|
// currentRequestCountLocked reports the number of concurrency slots currently in use,
|
||||||
// including active streams, reserved slots, and reset streams waiting for acknowledgement.
|
// including active streams, reserved slots, and reset streams waiting for acknowledgement.
|
||||||
func (cc *ClientConn) currentRequestCountLocked() int {
|
func (cc *ClientConn) currentRequestCountLocked() int {
|
||||||
|
|
@ -1066,6 +1103,14 @@ func (cc *ClientConn) canTakeNewRequestLocked() bool {
|
||||||
return st.canTakeNewRequest
|
return st.canTakeNewRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// availableLocked reports the number of concurrency slots available.
|
||||||
|
func (cc *ClientConn) availableLocked() int {
|
||||||
|
if !cc.canTakeNewRequestLocked() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return max(0, int(cc.maxConcurrentStreams)-cc.currentRequestCountLocked())
|
||||||
|
}
|
||||||
|
|
||||||
// tooIdleLocked reports whether this connection has been been sitting idle
|
// tooIdleLocked reports whether this connection has been been sitting idle
|
||||||
// for too much wall time.
|
// for too much wall time.
|
||||||
func (cc *ClientConn) tooIdleLocked() bool {
|
func (cc *ClientConn) tooIdleLocked() bool {
|
||||||
|
|
@ -1090,6 +1135,7 @@ func (cc *ClientConn) closeConn() {
|
||||||
t := time.AfterFunc(250*time.Millisecond, cc.forceCloseConn)
|
t := time.AfterFunc(250*time.Millisecond, cc.forceCloseConn)
|
||||||
defer t.Stop()
|
defer t.Stop()
|
||||||
cc.tconn.Close()
|
cc.tconn.Close()
|
||||||
|
cc.maybeCallStateHook()
|
||||||
}
|
}
|
||||||
|
|
||||||
// A tls.Conn.Close can hang for a long time if the peer is unresponsive.
|
// A tls.Conn.Close can hang for a long time if the peer is unresponsive.
|
||||||
|
|
@ -1615,6 +1661,8 @@ func (cs *clientStream) cleanupWriteRequest(err error) {
|
||||||
}
|
}
|
||||||
bodyClosed := cs.reqBodyClosed
|
bodyClosed := cs.reqBodyClosed
|
||||||
closeOnIdle := cc.singleUse || cc.doNotReuse || cc.t.disableKeepAlives() || cc.goAway != nil
|
closeOnIdle := cc.singleUse || cc.doNotReuse || cc.t.disableKeepAlives() || cc.goAway != nil
|
||||||
|
// Have we read any frames from the connection since sending this request?
|
||||||
|
readSinceStream := cc.readBeforeStreamID > cs.ID
|
||||||
cc.mu.Unlock()
|
cc.mu.Unlock()
|
||||||
if mustCloseBody {
|
if mustCloseBody {
|
||||||
cs.reqBody.Close()
|
cs.reqBody.Close()
|
||||||
|
|
@ -1646,8 +1694,10 @@ func (cs *clientStream) cleanupWriteRequest(err error) {
|
||||||
//
|
//
|
||||||
// This could be due to the server becoming unresponsive.
|
// This could be due to the server becoming unresponsive.
|
||||||
// To avoid sending too many requests on a dead connection,
|
// To avoid sending too many requests on a dead connection,
|
||||||
// we let the request continue to consume a concurrency slot
|
// if we haven't read any frames from the connection since
|
||||||
// until we can confirm the server is still responding.
|
// sending this request, we let it continue to consume
|
||||||
|
// a concurrency slot until we can confirm the server is
|
||||||
|
// still responding.
|
||||||
// We do this by sending a PING frame along with the RST_STREAM
|
// We do this by sending a PING frame along with the RST_STREAM
|
||||||
// (unless a ping is already in flight).
|
// (unless a ping is already in flight).
|
||||||
//
|
//
|
||||||
|
|
@ -1658,7 +1708,7 @@ func (cs *clientStream) cleanupWriteRequest(err error) {
|
||||||
// because it's short lived and will probably be closed before
|
// because it's short lived and will probably be closed before
|
||||||
// we get the ping response.
|
// we get the ping response.
|
||||||
ping := false
|
ping := false
|
||||||
if !closeOnIdle {
|
if !closeOnIdle && !readSinceStream {
|
||||||
cc.mu.Lock()
|
cc.mu.Lock()
|
||||||
// rstStreamPingsBlocked works around a gRPC behavior:
|
// rstStreamPingsBlocked works around a gRPC behavior:
|
||||||
// see comment on the field for details.
|
// see comment on the field for details.
|
||||||
|
|
@ -1692,6 +1742,7 @@ func (cs *clientStream) cleanupWriteRequest(err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
close(cs.donec)
|
close(cs.donec)
|
||||||
|
cc.maybeCallStateHook()
|
||||||
}
|
}
|
||||||
|
|
||||||
// awaitOpenSlotForStreamLocked waits until len(streams) < maxConcurrentStreams.
|
// awaitOpenSlotForStreamLocked waits until len(streams) < maxConcurrentStreams.
|
||||||
|
|
@ -2744,6 +2795,7 @@ func (rl *clientConnReadLoop) streamByID(id uint32, headerOrData bool) *clientSt
|
||||||
// See comment on ClientConn.rstStreamPingsBlocked for details.
|
// See comment on ClientConn.rstStreamPingsBlocked for details.
|
||||||
rl.cc.rstStreamPingsBlocked = false
|
rl.cc.rstStreamPingsBlocked = false
|
||||||
}
|
}
|
||||||
|
rl.cc.readBeforeStreamID = rl.cc.nextStreamID
|
||||||
cs := rl.cc.streams[id]
|
cs := rl.cc.streams[id]
|
||||||
if cs != nil && !cs.readAborted {
|
if cs != nil && !cs.readAborted {
|
||||||
return cs
|
return cs
|
||||||
|
|
@ -2794,6 +2846,7 @@ func (rl *clientConnReadLoop) processSettings(f *SettingsFrame) error {
|
||||||
|
|
||||||
func (rl *clientConnReadLoop) processSettingsNoWrite(f *SettingsFrame) error {
|
func (rl *clientConnReadLoop) processSettingsNoWrite(f *SettingsFrame) error {
|
||||||
cc := rl.cc
|
cc := rl.cc
|
||||||
|
defer cc.maybeCallStateHook()
|
||||||
cc.mu.Lock()
|
cc.mu.Lock()
|
||||||
defer cc.mu.Unlock()
|
defer cc.mu.Unlock()
|
||||||
|
|
||||||
|
|
@ -2974,6 +3027,7 @@ func (cc *ClientConn) Ping(ctx context.Context) error {
|
||||||
func (rl *clientConnReadLoop) processPing(f *PingFrame) error {
|
func (rl *clientConnReadLoop) processPing(f *PingFrame) error {
|
||||||
if f.IsAck() {
|
if f.IsAck() {
|
||||||
cc := rl.cc
|
cc := rl.cc
|
||||||
|
defer cc.maybeCallStateHook()
|
||||||
cc.mu.Lock()
|
cc.mu.Lock()
|
||||||
defer cc.mu.Unlock()
|
defer cc.mu.Unlock()
|
||||||
// If ack, notify listener if any
|
// If ack, notify listener if any
|
||||||
|
|
@ -3076,35 +3130,102 @@ type erringRoundTripper struct{ err error }
|
||||||
func (rt erringRoundTripper) RoundTripErr() error { return rt.err }
|
func (rt erringRoundTripper) RoundTripErr() error { return rt.err }
|
||||||
func (rt erringRoundTripper) RoundTrip(*http.Request) (*http.Response, error) { return nil, rt.err }
|
func (rt erringRoundTripper) RoundTrip(*http.Request) (*http.Response, error) { return nil, rt.err }
|
||||||
|
|
||||||
|
var errConcurrentReadOnResBody = errors.New("http2: concurrent read on response body")
|
||||||
|
|
||||||
// gzipReader wraps a response body so it can lazily
|
// gzipReader wraps a response body so it can lazily
|
||||||
// call gzip.NewReader on the first call to Read
|
// get gzip.Reader from the pool on the first call to Read.
|
||||||
|
// After Close is called it puts gzip.Reader to the pool immediately
|
||||||
|
// if there is no Read in progress or later when Read completes.
|
||||||
type gzipReader struct {
|
type gzipReader struct {
|
||||||
_ incomparable
|
_ incomparable
|
||||||
body io.ReadCloser // underlying Response.Body
|
body io.ReadCloser // underlying Response.Body
|
||||||
zr *gzip.Reader // lazily-initialized gzip reader
|
mu sync.Mutex // guards zr and zerr
|
||||||
zerr error // sticky error
|
zr *gzip.Reader // stores gzip reader from the pool between reads
|
||||||
|
zerr error // sticky gzip reader init error or sentinel value to detect concurrent read and read after close
|
||||||
|
}
|
||||||
|
|
||||||
|
type eofReader struct{}
|
||||||
|
|
||||||
|
func (eofReader) Read([]byte) (int, error) { return 0, io.EOF }
|
||||||
|
func (eofReader) ReadByte() (byte, error) { return 0, io.EOF }
|
||||||
|
|
||||||
|
var gzipPool = sync.Pool{New: func() any { return new(gzip.Reader) }}
|
||||||
|
|
||||||
|
// gzipPoolGet gets a gzip.Reader from the pool and resets it to read from r.
|
||||||
|
func gzipPoolGet(r io.Reader) (*gzip.Reader, error) {
|
||||||
|
zr := gzipPool.Get().(*gzip.Reader)
|
||||||
|
if err := zr.Reset(r); err != nil {
|
||||||
|
gzipPoolPut(zr)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return zr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// gzipPoolPut puts a gzip.Reader back into the pool.
|
||||||
|
func gzipPoolPut(zr *gzip.Reader) {
|
||||||
|
// Reset will allocate bufio.Reader if we pass it anything
|
||||||
|
// other than a flate.Reader, so ensure that it's getting one.
|
||||||
|
var r flate.Reader = eofReader{}
|
||||||
|
zr.Reset(r)
|
||||||
|
gzipPool.Put(zr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// acquire returns a gzip.Reader for reading response body.
|
||||||
|
// The reader must be released after use.
|
||||||
|
func (gz *gzipReader) acquire() (*gzip.Reader, error) {
|
||||||
|
gz.mu.Lock()
|
||||||
|
defer gz.mu.Unlock()
|
||||||
|
if gz.zerr != nil {
|
||||||
|
return nil, gz.zerr
|
||||||
|
}
|
||||||
|
if gz.zr == nil {
|
||||||
|
gz.zr, gz.zerr = gzipPoolGet(gz.body)
|
||||||
|
if gz.zerr != nil {
|
||||||
|
return nil, gz.zerr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret := gz.zr
|
||||||
|
gz.zr, gz.zerr = nil, errConcurrentReadOnResBody
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// release returns the gzip.Reader to the pool if Close was called during Read.
|
||||||
|
func (gz *gzipReader) release(zr *gzip.Reader) {
|
||||||
|
gz.mu.Lock()
|
||||||
|
defer gz.mu.Unlock()
|
||||||
|
if gz.zerr == errConcurrentReadOnResBody {
|
||||||
|
gz.zr, gz.zerr = zr, nil
|
||||||
|
} else { // fs.ErrClosed
|
||||||
|
gzipPoolPut(zr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// close returns the gzip.Reader to the pool immediately or
|
||||||
|
// signals release to do so after Read completes.
|
||||||
|
func (gz *gzipReader) close() {
|
||||||
|
gz.mu.Lock()
|
||||||
|
defer gz.mu.Unlock()
|
||||||
|
if gz.zerr == nil && gz.zr != nil {
|
||||||
|
gzipPoolPut(gz.zr)
|
||||||
|
gz.zr = nil
|
||||||
|
}
|
||||||
|
gz.zerr = fs.ErrClosed
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gz *gzipReader) Read(p []byte) (n int, err error) {
|
func (gz *gzipReader) Read(p []byte) (n int, err error) {
|
||||||
if gz.zerr != nil {
|
zr, err := gz.acquire()
|
||||||
return 0, gz.zerr
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
}
|
}
|
||||||
if gz.zr == nil {
|
defer gz.release(zr)
|
||||||
gz.zr, err = gzip.NewReader(gz.body)
|
|
||||||
if err != nil {
|
return zr.Read(p)
|
||||||
gz.zerr = err
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return gz.zr.Read(p)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gz *gzipReader) Close() error {
|
func (gz *gzipReader) Close() error {
|
||||||
if err := gz.body.Close(); err != nil {
|
gz.close()
|
||||||
return err
|
|
||||||
}
|
return gz.body.Close()
|
||||||
gz.zerr = fs.ErrClosed
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type errorReader struct{ err error }
|
type errorReader struct{ err error }
|
||||||
|
|
@ -3130,9 +3251,13 @@ func registerHTTPSProtocol(t *http.Transport, rt noDialH2RoundTripper) (err erro
|
||||||
}
|
}
|
||||||
|
|
||||||
// noDialH2RoundTripper is a RoundTripper which only tries to complete the request
|
// noDialH2RoundTripper is a RoundTripper which only tries to complete the request
|
||||||
// if there's already has a cached connection to the host.
|
// if there's already a cached connection to the host.
|
||||||
// (The field is exported so it can be accessed via reflect from net/http; tested
|
// (The field is exported so it can be accessed via reflect from net/http; tested
|
||||||
// by TestNoDialH2RoundTripperType)
|
// by TestNoDialH2RoundTripperType)
|
||||||
|
//
|
||||||
|
// A noDialH2RoundTripper is registered with http1.Transport.RegisterProtocol,
|
||||||
|
// and the http1.Transport can use type assertions to call non-RoundTrip methods on it.
|
||||||
|
// This lets us expose, for example, NewClientConn to net/http.
|
||||||
type noDialH2RoundTripper struct{ *Transport }
|
type noDialH2RoundTripper struct{ *Transport }
|
||||||
|
|
||||||
func (rt noDialH2RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
func (rt noDialH2RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
|
|
@ -3143,6 +3268,85 @@ func (rt noDialH2RoundTripper) RoundTrip(req *http.Request) (*http.Response, err
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rt noDialH2RoundTripper) NewClientConn(conn net.Conn, internalStateHook func()) (http.RoundTripper, error) {
|
||||||
|
tr := rt.Transport
|
||||||
|
cc, err := tr.newClientConn(conn, tr.disableKeepAlives(), internalStateHook)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoundTrip should block when the conn is at its concurrency limit,
|
||||||
|
// not return an error. Setting strictMaxConcurrentStreams enables this.
|
||||||
|
cc.strictMaxConcurrentStreams = true
|
||||||
|
|
||||||
|
return netHTTPClientConn{cc}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// netHTTPClientConn wraps ClientConn and implements the interface net/http expects from
|
||||||
|
// the RoundTripper returned by NewClientConn.
|
||||||
|
type netHTTPClientConn struct {
|
||||||
|
cc *ClientConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cc netHTTPClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
|
return cc.cc.RoundTrip(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cc netHTTPClientConn) Close() error {
|
||||||
|
return cc.cc.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cc netHTTPClientConn) Err() error {
|
||||||
|
cc.cc.mu.Lock()
|
||||||
|
defer cc.cc.mu.Unlock()
|
||||||
|
if cc.cc.closed {
|
||||||
|
return errors.New("connection closed")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cc netHTTPClientConn) Reserve() error {
|
||||||
|
defer cc.cc.maybeCallStateHook()
|
||||||
|
cc.cc.mu.Lock()
|
||||||
|
defer cc.cc.mu.Unlock()
|
||||||
|
if !cc.cc.canReserveLocked() {
|
||||||
|
return errors.New("connection is unavailable")
|
||||||
|
}
|
||||||
|
cc.cc.streamsReserved++
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cc netHTTPClientConn) Release() {
|
||||||
|
defer cc.cc.maybeCallStateHook()
|
||||||
|
cc.cc.mu.Lock()
|
||||||
|
defer cc.cc.mu.Unlock()
|
||||||
|
// We don't complain if streamsReserved is 0.
|
||||||
|
//
|
||||||
|
// This is consistent with RoundTrip: both Release and RoundTrip will
|
||||||
|
// consume a reservation iff one exists.
|
||||||
|
if cc.cc.streamsReserved > 0 {
|
||||||
|
cc.cc.streamsReserved--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cc netHTTPClientConn) Available() int {
|
||||||
|
cc.cc.mu.Lock()
|
||||||
|
defer cc.cc.mu.Unlock()
|
||||||
|
return cc.cc.availableLocked()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cc netHTTPClientConn) InFlight() int {
|
||||||
|
cc.cc.mu.Lock()
|
||||||
|
defer cc.cc.mu.Unlock()
|
||||||
|
return cc.cc.currentRequestCountLocked()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cc *ClientConn) maybeCallStateHook() {
|
||||||
|
if cc.internalStateHook != nil {
|
||||||
|
cc.internalStateHook()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (t *Transport) idleConnTimeout() time.Duration {
|
func (t *Transport) idleConnTimeout() time.Duration {
|
||||||
// to keep things backwards compatible, we use non-zero values of
|
// to keep things backwards compatible, we use non-zero values of
|
||||||
// IdleConnTimeout, followed by using the IdleConnTimeout on the underlying
|
// IdleConnTimeout, followed by using the IdleConnTimeout on the underlying
|
||||||
|
|
|
||||||
65
vendor/golang.org/x/net/http2/writesched.go
generated
vendored
65
vendor/golang.org/x/net/http2/writesched.go
generated
vendored
|
|
@ -185,45 +185,75 @@ func (wr *FrameWriteRequest) replyToWriter(err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeQueue is used by implementations of WriteScheduler.
|
// writeQueue is used by implementations of WriteScheduler.
|
||||||
|
//
|
||||||
|
// Each writeQueue contains a queue of FrameWriteRequests, meant to store all
|
||||||
|
// FrameWriteRequests associated with a given stream. This is implemented as a
|
||||||
|
// two-stage queue: currQueue[currPos:] and nextQueue. Removing an item is done
|
||||||
|
// by incrementing currPos of currQueue. Adding an item is done by appending it
|
||||||
|
// to the nextQueue. If currQueue is empty when trying to remove an item, we
|
||||||
|
// can swap currQueue and nextQueue to remedy the situation.
|
||||||
|
// This two-stage queue is analogous to the use of two lists in Okasaki's
|
||||||
|
// purely functional queue but without the overhead of reversing the list when
|
||||||
|
// swapping stages.
|
||||||
|
//
|
||||||
|
// writeQueue also contains prev and next, this can be used by implementations
|
||||||
|
// of WriteScheduler to construct data structures that represent the order of
|
||||||
|
// writing between different streams (e.g. circular linked list).
|
||||||
type writeQueue struct {
|
type writeQueue struct {
|
||||||
s []FrameWriteRequest
|
currQueue []FrameWriteRequest
|
||||||
|
nextQueue []FrameWriteRequest
|
||||||
|
currPos int
|
||||||
|
|
||||||
prev, next *writeQueue
|
prev, next *writeQueue
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *writeQueue) empty() bool { return len(q.s) == 0 }
|
func (q *writeQueue) empty() bool {
|
||||||
|
return (len(q.currQueue) - q.currPos + len(q.nextQueue)) == 0
|
||||||
|
}
|
||||||
|
|
||||||
func (q *writeQueue) push(wr FrameWriteRequest) {
|
func (q *writeQueue) push(wr FrameWriteRequest) {
|
||||||
q.s = append(q.s, wr)
|
q.nextQueue = append(q.nextQueue, wr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *writeQueue) shift() FrameWriteRequest {
|
func (q *writeQueue) shift() FrameWriteRequest {
|
||||||
if len(q.s) == 0 {
|
if q.empty() {
|
||||||
panic("invalid use of queue")
|
panic("invalid use of queue")
|
||||||
}
|
}
|
||||||
wr := q.s[0]
|
if q.currPos >= len(q.currQueue) {
|
||||||
// TODO: less copy-happy queue.
|
q.currQueue, q.currPos, q.nextQueue = q.nextQueue, 0, q.currQueue[:0]
|
||||||
copy(q.s, q.s[1:])
|
}
|
||||||
q.s[len(q.s)-1] = FrameWriteRequest{}
|
wr := q.currQueue[q.currPos]
|
||||||
q.s = q.s[:len(q.s)-1]
|
q.currQueue[q.currPos] = FrameWriteRequest{}
|
||||||
|
q.currPos++
|
||||||
return wr
|
return wr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (q *writeQueue) peek() *FrameWriteRequest {
|
||||||
|
if q.currPos < len(q.currQueue) {
|
||||||
|
return &q.currQueue[q.currPos]
|
||||||
|
}
|
||||||
|
if len(q.nextQueue) > 0 {
|
||||||
|
return &q.nextQueue[0]
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// consume consumes up to n bytes from q.s[0]. If the frame is
|
// consume consumes up to n bytes from q.s[0]. If the frame is
|
||||||
// entirely consumed, it is removed from the queue. If the frame
|
// entirely consumed, it is removed from the queue. If the frame
|
||||||
// is partially consumed, the frame is kept with the consumed
|
// is partially consumed, the frame is kept with the consumed
|
||||||
// bytes removed. Returns true iff any bytes were consumed.
|
// bytes removed. Returns true iff any bytes were consumed.
|
||||||
func (q *writeQueue) consume(n int32) (FrameWriteRequest, bool) {
|
func (q *writeQueue) consume(n int32) (FrameWriteRequest, bool) {
|
||||||
if len(q.s) == 0 {
|
if q.empty() {
|
||||||
return FrameWriteRequest{}, false
|
return FrameWriteRequest{}, false
|
||||||
}
|
}
|
||||||
consumed, rest, numresult := q.s[0].Consume(n)
|
consumed, rest, numresult := q.peek().Consume(n)
|
||||||
switch numresult {
|
switch numresult {
|
||||||
case 0:
|
case 0:
|
||||||
return FrameWriteRequest{}, false
|
return FrameWriteRequest{}, false
|
||||||
case 1:
|
case 1:
|
||||||
q.shift()
|
q.shift()
|
||||||
case 2:
|
case 2:
|
||||||
q.s[0] = rest
|
*q.peek() = rest
|
||||||
}
|
}
|
||||||
return consumed, true
|
return consumed, true
|
||||||
}
|
}
|
||||||
|
|
@ -232,10 +262,15 @@ type writeQueuePool []*writeQueue
|
||||||
|
|
||||||
// put inserts an unused writeQueue into the pool.
|
// put inserts an unused writeQueue into the pool.
|
||||||
func (p *writeQueuePool) put(q *writeQueue) {
|
func (p *writeQueuePool) put(q *writeQueue) {
|
||||||
for i := range q.s {
|
for i := range q.currQueue {
|
||||||
q.s[i] = FrameWriteRequest{}
|
q.currQueue[i] = FrameWriteRequest{}
|
||||||
}
|
}
|
||||||
q.s = q.s[:0]
|
for i := range q.nextQueue {
|
||||||
|
q.nextQueue[i] = FrameWriteRequest{}
|
||||||
|
}
|
||||||
|
q.currQueue = q.currQueue[:0]
|
||||||
|
q.nextQueue = q.nextQueue[:0]
|
||||||
|
q.currPos = 0
|
||||||
*p = append(*p, q)
|
*p = append(*p, q)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
5
vendor/golang.org/x/net/http2/writesched_priority_rfc7540.go
generated
vendored
5
vendor/golang.org/x/net/http2/writesched_priority_rfc7540.go
generated
vendored
|
|
@ -214,8 +214,8 @@ func (z sortPriorityNodeSiblingsRFC7540) Swap(i, k int) { z[i], z[k] = z[k], z[i
|
||||||
func (z sortPriorityNodeSiblingsRFC7540) Less(i, k int) bool {
|
func (z sortPriorityNodeSiblingsRFC7540) Less(i, k int) bool {
|
||||||
// Prefer the subtree that has sent fewer bytes relative to its weight.
|
// Prefer the subtree that has sent fewer bytes relative to its weight.
|
||||||
// See sections 5.3.2 and 5.3.4.
|
// See sections 5.3.2 and 5.3.4.
|
||||||
wi, bi := float64(z[i].weight+1), float64(z[i].subtreeBytes)
|
wi, bi := float64(z[i].weight)+1, float64(z[i].subtreeBytes)
|
||||||
wk, bk := float64(z[k].weight+1), float64(z[k].subtreeBytes)
|
wk, bk := float64(z[k].weight)+1, float64(z[k].subtreeBytes)
|
||||||
if bi == 0 && bk == 0 {
|
if bi == 0 && bk == 0 {
|
||||||
return wi >= wk
|
return wi >= wk
|
||||||
}
|
}
|
||||||
|
|
@ -302,7 +302,6 @@ func (ws *priorityWriteSchedulerRFC7540) CloseStream(streamID uint32) {
|
||||||
|
|
||||||
q := n.q
|
q := n.q
|
||||||
ws.queuePool.put(&q)
|
ws.queuePool.put(&q)
|
||||||
n.q.s = nil
|
|
||||||
if ws.maxClosedNodesInTree > 0 {
|
if ws.maxClosedNodesInTree > 0 {
|
||||||
ws.addClosedOrIdleNode(&ws.closedNodes, ws.maxClosedNodesInTree, n)
|
ws.addClosedOrIdleNode(&ws.closedNodes, ws.maxClosedNodesInTree, n)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ type priorityWriteSchedulerRFC9218 struct {
|
||||||
prioritizeIncremental bool
|
prioritizeIncremental bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPriorityWriteSchedulerRFC9128() WriteScheduler {
|
func newPriorityWriteSchedulerRFC9218() WriteScheduler {
|
||||||
ws := &priorityWriteSchedulerRFC9218{
|
ws := &priorityWriteSchedulerRFC9218{
|
||||||
streams: make(map[uint32]streamMetadata),
|
streams: make(map[uint32]streamMetadata),
|
||||||
}
|
}
|
||||||
5
vendor/golang.org/x/net/publicsuffix/list.go
generated
vendored
5
vendor/golang.org/x/net/publicsuffix/list.go
generated
vendored
|
|
@ -51,6 +51,7 @@ package publicsuffix // import "golang.org/x/net/publicsuffix"
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http/cookiejar"
|
"net/http/cookiejar"
|
||||||
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -84,6 +85,10 @@ func (list) String() string {
|
||||||
// domains like "foo.appspot.com" can be found at
|
// domains like "foo.appspot.com" can be found at
|
||||||
// https://wiki.mozilla.org/Public_Suffix_List/Use_Cases
|
// https://wiki.mozilla.org/Public_Suffix_List/Use_Cases
|
||||||
func PublicSuffix(domain string) (publicSuffix string, icann bool) {
|
func PublicSuffix(domain string) (publicSuffix string, icann bool) {
|
||||||
|
if _, err := netip.ParseAddr(domain); err == nil {
|
||||||
|
return domain, false
|
||||||
|
}
|
||||||
|
|
||||||
lo, hi := uint32(0), uint32(numTLD)
|
lo, hi := uint32(0), uint32(numTLD)
|
||||||
s, suffix, icannNode, wildcard := domain, len(domain), false, false
|
s, suffix, icannNode, wildcard := domain, len(domain), false, false
|
||||||
loop:
|
loop:
|
||||||
|
|
|
||||||
12
vendor/golang.org/x/sys/cpu/cpu_arm64.s
generated
vendored
12
vendor/golang.org/x/sys/cpu/cpu_arm64.s
generated
vendored
|
|
@ -9,31 +9,27 @@
|
||||||
// func getisar0() uint64
|
// func getisar0() uint64
|
||||||
TEXT ·getisar0(SB),NOSPLIT,$0-8
|
TEXT ·getisar0(SB),NOSPLIT,$0-8
|
||||||
// get Instruction Set Attributes 0 into x0
|
// get Instruction Set Attributes 0 into x0
|
||||||
// mrs x0, ID_AA64ISAR0_EL1 = d5380600
|
MRS ID_AA64ISAR0_EL1, R0
|
||||||
WORD $0xd5380600
|
|
||||||
MOVD R0, ret+0(FP)
|
MOVD R0, ret+0(FP)
|
||||||
RET
|
RET
|
||||||
|
|
||||||
// func getisar1() uint64
|
// func getisar1() uint64
|
||||||
TEXT ·getisar1(SB),NOSPLIT,$0-8
|
TEXT ·getisar1(SB),NOSPLIT,$0-8
|
||||||
// get Instruction Set Attributes 1 into x0
|
// get Instruction Set Attributes 1 into x0
|
||||||
// mrs x0, ID_AA64ISAR1_EL1 = d5380620
|
MRS ID_AA64ISAR1_EL1, R0
|
||||||
WORD $0xd5380620
|
|
||||||
MOVD R0, ret+0(FP)
|
MOVD R0, ret+0(FP)
|
||||||
RET
|
RET
|
||||||
|
|
||||||
// func getpfr0() uint64
|
// func getpfr0() uint64
|
||||||
TEXT ·getpfr0(SB),NOSPLIT,$0-8
|
TEXT ·getpfr0(SB),NOSPLIT,$0-8
|
||||||
// get Processor Feature Register 0 into x0
|
// get Processor Feature Register 0 into x0
|
||||||
// mrs x0, ID_AA64PFR0_EL1 = d5380400
|
MRS ID_AA64PFR0_EL1, R0
|
||||||
WORD $0xd5380400
|
|
||||||
MOVD R0, ret+0(FP)
|
MOVD R0, ret+0(FP)
|
||||||
RET
|
RET
|
||||||
|
|
||||||
// func getzfr0() uint64
|
// func getzfr0() uint64
|
||||||
TEXT ·getzfr0(SB),NOSPLIT,$0-8
|
TEXT ·getzfr0(SB),NOSPLIT,$0-8
|
||||||
// get SVE Feature Register 0 into x0
|
// get SVE Feature Register 0 into x0
|
||||||
// mrs x0, ID_AA64ZFR0_EL1 = d5380480
|
MRS ID_AA64ZFR0_EL1, R0
|
||||||
WORD $0xd5380480
|
|
||||||
MOVD R0, ret+0(FP)
|
MOVD R0, ret+0(FP)
|
||||||
RET
|
RET
|
||||||
|
|
|
||||||
5
vendor/golang.org/x/sys/unix/mkerrors.sh
generated
vendored
5
vendor/golang.org/x/sys/unix/mkerrors.sh
generated
vendored
|
|
@ -226,6 +226,7 @@ struct ltchars {
|
||||||
#include <linux/cryptouser.h>
|
#include <linux/cryptouser.h>
|
||||||
#include <linux/devlink.h>
|
#include <linux/devlink.h>
|
||||||
#include <linux/dm-ioctl.h>
|
#include <linux/dm-ioctl.h>
|
||||||
|
#include <linux/elf.h>
|
||||||
#include <linux/errqueue.h>
|
#include <linux/errqueue.h>
|
||||||
#include <linux/ethtool_netlink.h>
|
#include <linux/ethtool_netlink.h>
|
||||||
#include <linux/falloc.h>
|
#include <linux/falloc.h>
|
||||||
|
|
@ -255,6 +256,7 @@ struct ltchars {
|
||||||
#include <linux/loop.h>
|
#include <linux/loop.h>
|
||||||
#include <linux/lwtunnel.h>
|
#include <linux/lwtunnel.h>
|
||||||
#include <linux/magic.h>
|
#include <linux/magic.h>
|
||||||
|
#include <linux/mei.h>
|
||||||
#include <linux/memfd.h>
|
#include <linux/memfd.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/mount.h>
|
#include <linux/mount.h>
|
||||||
|
|
@ -529,6 +531,7 @@ ccflags="$@"
|
||||||
$2 ~ /^O[CNPFPL][A-Z]+[^_][A-Z]+$/ ||
|
$2 ~ /^O[CNPFPL][A-Z]+[^_][A-Z]+$/ ||
|
||||||
$2 ~ /^(NL|CR|TAB|BS|VT|FF)DLY$/ ||
|
$2 ~ /^(NL|CR|TAB|BS|VT|FF)DLY$/ ||
|
||||||
$2 ~ /^(NL|CR|TAB|BS|VT|FF)[0-9]$/ ||
|
$2 ~ /^(NL|CR|TAB|BS|VT|FF)[0-9]$/ ||
|
||||||
|
$2 ~ /^(DT|EI|ELF|EV|NN|NT|PF|SHF|SHN|SHT|STB|STT|VER)_/ ||
|
||||||
$2 ~ /^O?XTABS$/ ||
|
$2 ~ /^O?XTABS$/ ||
|
||||||
$2 ~ /^TC[IO](ON|OFF)$/ ||
|
$2 ~ /^TC[IO](ON|OFF)$/ ||
|
||||||
$2 ~ /^IN_/ ||
|
$2 ~ /^IN_/ ||
|
||||||
|
|
@ -611,7 +614,7 @@ ccflags="$@"
|
||||||
$2 !~ /IOC_MAGIC/ &&
|
$2 !~ /IOC_MAGIC/ &&
|
||||||
$2 ~ /^[A-Z][A-Z0-9_]+_MAGIC2?$/ ||
|
$2 ~ /^[A-Z][A-Z0-9_]+_MAGIC2?$/ ||
|
||||||
$2 ~ /^(VM|VMADDR)_/ ||
|
$2 ~ /^(VM|VMADDR)_/ ||
|
||||||
$2 ~ /^IOCTL_VM_SOCKETS_/ ||
|
$2 ~ /^(IOCTL_VM_SOCKETS_|IOCTL_MEI_)/ ||
|
||||||
$2 ~ /^(TASKSTATS|TS)_/ ||
|
$2 ~ /^(TASKSTATS|TS)_/ ||
|
||||||
$2 ~ /^CGROUPSTATS_/ ||
|
$2 ~ /^CGROUPSTATS_/ ||
|
||||||
$2 ~ /^GENL_/ ||
|
$2 ~ /^GENL_/ ||
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue