Add SQLite support, fix un-thread-safe DB caches, small performance f… (#172)

* Add SQLite support, fix un-thread-safe DB caches, small performance fixes

Signed-off-by: kim (grufwub) <grufwub@gmail.com>

* add SQLite licenses to README

Signed-off-by: kim (grufwub) <grufwub@gmail.com>

* appease the linter, and fix my dumbass-ery

Signed-off-by: kim (grufwub) <grufwub@gmail.com>

* make requested changes

Signed-off-by: kim (grufwub) <grufwub@gmail.com>

* add back comment

Signed-off-by: kim (grufwub) <grufwub@gmail.com>
This commit is contained in:
kim 2021-08-29 15:41:41 +01:00 committed by GitHub
parent 53507ac2a3
commit ed46224573
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
730 changed files with 2239881 additions and 3669 deletions

View file

@ -148,6 +148,9 @@ The following libraries and frameworks are used by GoToSocial, with gratitude
* [uptrace/bun](https://github.com/uptrace/bun); database ORM. [BSD-2-Clause License](https://spdx.org/licenses/BSD-2-Clause.html). * [uptrace/bun](https://github.com/uptrace/bun); database ORM. [BSD-2-Clause License](https://spdx.org/licenses/BSD-2-Clause.html).
* [urfave/cli](https://github.com/urfave/cli); command-line interface framework. [MIT License](https://spdx.org/licenses/MIT.html). * [urfave/cli](https://github.com/urfave/cli); command-line interface framework. [MIT License](https://spdx.org/licenses/MIT.html).
* [wagslane/go-password-validator](https://github.com/wagslane/go-password-validator); password strength validation. [MIT License](https://spdx.org/licenses/MIT.html). * [wagslane/go-password-validator](https://github.com/wagslane/go-password-validator); password strength validation. [MIT License](https://spdx.org/licenses/MIT.html).
* [modernc.org/sqlite](sqlite); cgo-free port of SQLite. [Other License](https://gitlab.com/cznic/sqlite/-/blob/master/LICENSE).
* [modernc.org/ccgo](ccgo); c99 AST -> Go translater. [BSD-3-Clause License](https://spdx.org/licenses/BSD-3-Clause.html).
* [modernc.org/libc](libc); C-runtime services. [BSD-3-Clause License](https://spdx.org/licenses/BSD-3-Clause.html).
### Image Attribution ### Image Attribution

9
go.mod
View file

@ -29,6 +29,7 @@ require (
github.com/gorilla/sessions v1.2.1 // indirect github.com/gorilla/sessions v1.2.1 // indirect
github.com/gorilla/websocket v1.4.2 github.com/gorilla/websocket v1.4.2
github.com/h2non/filetype v1.1.1 github.com/h2non/filetype v1.1.1
github.com/jackc/pgconn v1.10.0
github.com/jackc/pgx/v4 v4.13.0 github.com/jackc/pgx/v4 v4.13.0
github.com/json-iterator/go v1.1.11 // indirect github.com/json-iterator/go v1.1.11 // indirect
github.com/leodido/go-urn v1.2.1 // indirect github.com/leodido/go-urn v1.2.1 // indirect
@ -48,15 +49,21 @@ require (
github.com/tidwall/buntdb v1.2.4 // indirect github.com/tidwall/buntdb v1.2.4 // indirect
github.com/uptrace/bun v0.4.3 github.com/uptrace/bun v0.4.3
github.com/uptrace/bun/dialect/pgdialect v0.4.3 github.com/uptrace/bun/dialect/pgdialect v0.4.3
github.com/uptrace/bun/dialect/sqlitedialect v0.4.3
github.com/urfave/cli/v2 v2.3.0 github.com/urfave/cli/v2 v2.3.0
github.com/wagslane/go-password-validator v0.3.0 github.com/wagslane/go-password-validator v0.3.0
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
golang.org/x/mod v0.5.0 // indirect
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914 golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf // indirect
golang.org/x/text v0.3.6 golang.org/x/text v0.3.6
golang.org/x/tools v0.1.5 // indirect
google.golang.org/appengine v1.6.7 // indirect google.golang.org/appengine v1.6.7 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
modernc.org/ccgo/v3 v3.10.1 // indirect
modernc.org/libc v1.10.0 // indirect
modernc.org/sqlite v1.12.0
mvdan.cc/xurls/v2 v2.3.0 mvdan.cc/xurls/v2 v2.3.0
) )

64
go.sum
View file

@ -102,6 +102,7 @@ github.com/dsoprea/go-utility v0.0.0-20200711062821-fab8125e9bdf/go.mod h1:95+K3
github.com/dsoprea/go-utility v0.0.0-20200717064901-2fccff4aa15e h1:ojqYA1mU6LuRm8XzrVOvyfb000y59cbUcu6Wt8sFSAs= github.com/dsoprea/go-utility v0.0.0-20200717064901-2fccff4aa15e h1:ojqYA1mU6LuRm8XzrVOvyfb000y59cbUcu6Wt8sFSAs=
github.com/dsoprea/go-utility v0.0.0-20200717064901-2fccff4aa15e/go.mod h1:KVK+/Hul09ujXAGq+42UBgCTnXkiJZRnLYdURGjQUwo= github.com/dsoprea/go-utility v0.0.0-20200717064901-2fccff4aa15e/go.mod h1:KVK+/Hul09ujXAGq+42UBgCTnXkiJZRnLYdURGjQUwo=
github.com/dsoprea/go-utility/v2 v2.0.0-20200717064901-2fccff4aa15e/go.mod h1:uAzdkPTub5Y9yQwXe8W4m2XuP0tK4a9Q/dantD0+uaU= github.com/dsoprea/go-utility/v2 v2.0.0-20200717064901-2fccff4aa15e/go.mod h1:uAzdkPTub5Y9yQwXe8W4m2XuP0tK4a9Q/dantD0+uaU=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@ -202,6 +203,7 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
@ -303,6 +305,8 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kidstuff/mongostore v0.0.0-20181113001930-e650cd85ee4b/go.mod h1:g2nVr8KZVXJSS97Jo8pJ0jgq29P6H7dG0oplUA86MQw= github.com/kidstuff/mongostore v0.0.0-20181113001930-e650cd85ee4b/go.mod h1:g2nVr8KZVXJSS97Jo8pJ0jgq29P6H7dG0oplUA86MQw=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.10.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.10.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
@ -336,6 +340,8 @@ github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2y
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA= github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-sqlite3 v1.14.8 h1:gDp86IdQsN/xWjIEmr9MF6o9mpksUgh0fu+9ByFxzIU=
github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/memcachier/mc v2.0.1+incompatible/go.mod h1:7bkvFE61leUBvXz+yxsOnGBQSZpBSPIMUQSmmSHvuXc= github.com/memcachier/mc v2.0.1+incompatible/go.mod h1:7bkvFE61leUBvXz+yxsOnGBQSZpBSPIMUQSmmSHvuXc=
github.com/microcosm-cc/bluemonday v1.0.15 h1:J4uN+qPng9rvkBZBoBb8YGR+ijuklIMpSOZZLjYpbeY= github.com/microcosm-cc/bluemonday v1.0.15 h1:J4uN+qPng9rvkBZBoBb8YGR+ijuklIMpSOZZLjYpbeY=
github.com/microcosm-cc/bluemonday v1.0.15/go.mod h1:ZLvAzeakRwrGnzQEvstVzVt3ZpqOF2+sdFr0Om+ce30= github.com/microcosm-cc/bluemonday v1.0.15/go.mod h1:ZLvAzeakRwrGnzQEvstVzVt3ZpqOF2+sdFr0Om+ce30=
@ -366,6 +372,8 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
github.com/quasoft/memstore v0.0.0-20180925164028-84a050167438/go.mod h1:wTPjTepVu7uJBYgZ0SdWHQlIas582j6cn2jgk4DDdlg= github.com/quasoft/memstore v0.0.0-20180925164028-84a050167438/go.mod h1:wTPjTepVu7uJBYgZ0SdWHQlIas582j6cn2jgk4DDdlg=
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b h1:aUNXCGgukb4gtY99imuIeoh8Vr0GSwAlYxPAhqZrpFc= github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b h1:aUNXCGgukb4gtY99imuIeoh8Vr0GSwAlYxPAhqZrpFc=
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b/go.mod h1:wTPjTepVu7uJBYgZ0SdWHQlIas582j6cn2jgk4DDdlg= github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b/go.mod h1:wTPjTepVu7uJBYgZ0SdWHQlIas582j6cn2jgk4DDdlg=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
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.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
@ -450,6 +458,8 @@ github.com/uptrace/bun v0.4.3 h1:x6bjDqwjxwM/9Q1eauhkznuvTrz/rLiCK2p4tT63sAE=
github.com/uptrace/bun v0.4.3/go.mod h1:aL6D9vPw8DXaTQTwGrEPtUderBYXx7ShUmPfnxnqscw= github.com/uptrace/bun v0.4.3/go.mod h1:aL6D9vPw8DXaTQTwGrEPtUderBYXx7ShUmPfnxnqscw=
github.com/uptrace/bun/dialect/pgdialect v0.4.3 h1:lM2IUKpU99110chKkupw3oTfXiOKpB0hTJIe6frqQDo= github.com/uptrace/bun/dialect/pgdialect v0.4.3 h1:lM2IUKpU99110chKkupw3oTfXiOKpB0hTJIe6frqQDo=
github.com/uptrace/bun/dialect/pgdialect v0.4.3/go.mod h1:BaNvWejl32oKUhwpFkw/eNcWldzIlVY4nfw/sNul0s8= github.com/uptrace/bun/dialect/pgdialect v0.4.3/go.mod h1:BaNvWejl32oKUhwpFkw/eNcWldzIlVY4nfw/sNul0s8=
github.com/uptrace/bun/dialect/sqlitedialect v0.4.3 h1:h+vqLGCeY22PFrbCOpQqK5+/p1qWCXYIhIUm/D5Vw08=
github.com/uptrace/bun/dialect/sqlitedialect v0.4.3/go.mod h1:W9zHzVl9lJwWHx038vHs7ddVJ6LbNebseLZ3UGAqeAk=
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
@ -479,6 +489,7 @@ github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZ
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
@ -542,6 +553,8 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.0 h1:UG21uOlmZabA4fW5i7ZX6bjw1xELEGg/ZLgZq9auk/Q=
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -573,6 +586,7 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q= golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
@ -592,6 +606,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180525142821-c11f84a56e43/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180525142821-c11f84a56e43/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -632,14 +647,17 @@ golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf h1:2ucpDCmfkl8Bd/FsLtiD653Wf96cW37s+iGx93zsu4k=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
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=
@ -700,7 +718,10 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -818,6 +839,45 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
lukechampine.com/uint128 v1.1.1 h1:pnxCASz787iMf+02ssImqk6OLt+Z5QHMoZyUXR4z6JU=
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.33.7/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.33.11 h1:Fc7goiKCzfHvGR4WZbVLWIh/4VhJE2Z31Jkg36Ezp7Q=
modernc.org/cc/v3 v3.33.11/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60=
modernc.org/ccgo/v3 v3.9.6/go.mod h1:KGOi0NhaT6CO19xeSXcpXBl0OkoD6T1U4dPd633G9Sg=
modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw=
modernc.org/ccgo/v3 v3.10.1 h1:iS/P/unYVUpy7aAxy4Xj8BTy8afdyN1P54Ez2LlUmFI=
modernc.org/ccgo/v3 v3.10.1/go.mod h1:Z+DnGxGOZEvVjdehbO78XHAIWGxyTxzuz668w3SgiiQ=
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
modernc.org/libc v1.7.13-0.20210308123627-12f642a52bb8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q=
modernc.org/libc v1.10.0 h1:VY0vRYW7BMx9vhF3ZvdgmjFOdRrV3EpjUFlRu+5O9FM=
modernc.org/libc v1.10.0/go.mod h1:0/Nct1oFfLhjihlkmiJvALxOyV2rlkJ7/OZk1ni+WDQ=
modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8=
modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc=
modernc.org/memory v1.0.5 h1:XRch8trV7GgvTec2i7jc33YlUI0RKVDBvZ5eZ5m8y14=
modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM=
modernc.org/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A=
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sqlite v1.12.0 h1:AMAOgk4CkblRJc6YLKSYtz3pZ6DW5wjQ1uYH/rN7/Kk=
modernc.org/sqlite v1.12.0/go.mod h1:ppqJ4cQ+R09YLzl9haEL9AYgj6wX8FcfwDTOI0nYykU=
modernc.org/strutil v1.1.1 h1:xv+J1BXY3Opl2ALrBwyfEikFAj8pmqcpnfmuwUwcozs=
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
modernc.org/tcl v1.5.5 h1:N03RwthgTR/l/eQvz3UjfYnvVVj1G2sZqzFGfoD4HE4=
modernc.org/tcl v1.5.5/go.mod h1:ADkaTUuwukkrlhqwERyq0SM8OvyXo7+TjFz7yAF56EI=
modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk=
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/z v1.0.1 h1:WyIDpEpAIx4Hel6q/Pcgj/VhaQV5XPJ2I6ryIYbjnpc=
modernc.org/z v1.0.1/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA=
mvdan.cc/xurls/v2 v2.3.0 h1:59Olnbt67UKpxF1EwVBopJvkSUBmgtb468E4GVWIZ1I= mvdan.cc/xurls/v2 v2.3.0 h1:59Olnbt67UKpxF1EwVBopJvkSUBmgtb468E4GVWIZ1I=
mvdan.cc/xurls/v2 v2.3.0/go.mod h1:AjuTy7gEiUArFMjgBBDU4SMxlfUYsRokpJQgNWOt3e4= mvdan.cc/xurls/v2 v2.3.0/go.mod h1:AjuTy7gEiUArFMjgBBDU4SMxlfUYsRokpJQgNWOt3e4=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

106
internal/cache/status.go vendored Normal file
View file

@ -0,0 +1,106 @@
package cache
import (
"sync"
"github.com/ReneKroon/ttlcache"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
)
// statusCache is a wrapper around ttlcache.Cache to provide URL and URI lookups for gtsmodel.Status
type StatusCache struct {
cache *ttlcache.Cache // map of IDs -> cached statuses
urls map[string]string // map of status URLs -> IDs
uris map[string]string // map of status URIs -> IDs
mutex sync.Mutex
}
// newStatusCache returns a new instantiated statusCache object
func NewStatusCache() *StatusCache {
c := StatusCache{
cache: ttlcache.NewCache(),
urls: make(map[string]string, 100),
uris: make(map[string]string, 100),
mutex: sync.Mutex{},
}
// Set callback to purge lookup maps on expiration
c.cache.SetExpirationCallback(func(key string, value interface{}) {
status := value.(*gtsmodel.Status)
c.mutex.Lock()
delete(c.urls, status.URL)
delete(c.uris, status.URI)
c.mutex.Unlock()
})
return &c
}
// GetByID attempts to fetch a status from the cache by its ID
func (c *StatusCache) GetByID(id string) (*gtsmodel.Status, bool) {
c.mutex.Lock()
status, ok := c.getByID(id)
c.mutex.Unlock()
return status, ok
}
// GetByURL attempts to fetch a status from the cache by its URL
func (c *StatusCache) GetByURL(url string) (*gtsmodel.Status, bool) {
// Perform safe ID lookup
c.mutex.Lock()
id, ok := c.urls[url]
// Not found, unlock early
if !ok {
c.mutex.Unlock()
return nil, false
}
// Attempt status lookup
status, ok := c.getByID(id)
c.mutex.Unlock()
return status, ok
}
// GetByURI attempts to fetch a status from the cache by its URI
func (c *StatusCache) GetByURI(uri string) (*gtsmodel.Status, bool) {
// Perform safe ID lookup
c.mutex.Lock()
id, ok := c.uris[uri]
// Not found, unlock early
if !ok {
c.mutex.Unlock()
return nil, false
}
// Attempt status lookup
status, ok := c.getByID(id)
c.mutex.Unlock()
return status, ok
}
// getByID performs an unsafe (no mutex locks) lookup of status by ID
func (c *StatusCache) getByID(id string) (*gtsmodel.Status, bool) {
v, ok := c.cache.Get(id)
if !ok {
return nil, false
}
return v.(*gtsmodel.Status), true
}
// Put places a status in the cache
func (c *StatusCache) Put(status *gtsmodel.Status) {
if status == nil || status.ID == "" ||
status.URL == "" ||
status.URI == "" {
panic("invalid status")
}
c.mutex.Lock()
c.cache.Set(status.ID, status)
c.urls[status.URL] = status.ID
c.uris[status.URI] = status.ID
c.mutex.Unlock()
}

41
internal/cache/status_test.go vendored Normal file
View file

@ -0,0 +1,41 @@
package cache_test
import (
"testing"
"github.com/superseriousbusiness/gotosocial/internal/cache"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
)
func TestStatusCache(t *testing.T) {
cache := cache.NewStatusCache()
// Attempt to place a status
status := gtsmodel.Status{
ID: "id",
URI: "uri",
URL: "url",
}
cache.Put(&status)
var ok bool
var check *gtsmodel.Status
// Check we can retrieve
check, ok = cache.GetByID(status.ID)
if !ok || !statusIs(&status, check) {
t.Fatal("Could not find expected status")
}
check, ok = cache.GetByURI(status.URI)
if !ok || !statusIs(&status, check) {
t.Fatal("Could not find expected status")
}
check, ok = cache.GetByURL(status.URL)
if !ok || !statusIs(&status, check) {
t.Fatal("Could not find expected status")
}
}
func statusIs(status1, status2 *gtsmodel.Status) bool {
return status1.ID == status2.ID && status1.URI == status2.URI && status1.URL == status2.URL
}

View file

@ -25,7 +25,6 @@ import (
"strings" "strings"
"time" "time"
"github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
@ -34,8 +33,7 @@ import (
type accountDB struct { type accountDB struct {
config *config.Config config *config.Config
conn *bun.DB conn *DBConn
log *logrus.Logger
} }
func (a *accountDB) newAccountQ(account *gtsmodel.Account) *bun.SelectQuery { func (a *accountDB) newAccountQ(account *gtsmodel.Account) *bun.SelectQuery {
@ -52,9 +50,11 @@ func (a *accountDB) GetAccountByID(ctx context.Context, id string) (*gtsmodel.Ac
q := a.newAccountQ(account). q := a.newAccountQ(account).
Where("account.id = ?", id) Where("account.id = ?", id)
err := processErrorResponse(q.Scan(ctx)) err := q.Scan(ctx)
if err != nil {
return account, err return nil, a.conn.ProcessError(err)
}
return account, nil
} }
func (a *accountDB) GetAccountByURI(ctx context.Context, uri string) (*gtsmodel.Account, db.Error) { func (a *accountDB) GetAccountByURI(ctx context.Context, uri string) (*gtsmodel.Account, db.Error) {
@ -63,9 +63,11 @@ func (a *accountDB) GetAccountByURI(ctx context.Context, uri string) (*gtsmodel.
q := a.newAccountQ(account). q := a.newAccountQ(account).
Where("account.uri = ?", uri) Where("account.uri = ?", uri)
err := processErrorResponse(q.Scan(ctx)) err := q.Scan(ctx)
if err != nil {
return account, err return nil, a.conn.ProcessError(err)
}
return account, nil
} }
func (a *accountDB) GetAccountByURL(ctx context.Context, uri string) (*gtsmodel.Account, db.Error) { func (a *accountDB) GetAccountByURL(ctx context.Context, uri string) (*gtsmodel.Account, db.Error) {
@ -74,9 +76,11 @@ func (a *accountDB) GetAccountByURL(ctx context.Context, uri string) (*gtsmodel.
q := a.newAccountQ(account). q := a.newAccountQ(account).
Where("account.url = ?", uri) Where("account.url = ?", uri)
err := processErrorResponse(q.Scan(ctx)) err := q.Scan(ctx)
if err != nil {
return account, err return nil, a.conn.ProcessError(err)
}
return account, nil
} }
func (a *accountDB) UpdateAccount(ctx context.Context, account *gtsmodel.Account) (*gtsmodel.Account, db.Error) { func (a *accountDB) UpdateAccount(ctx context.Context, account *gtsmodel.Account) (*gtsmodel.Account, db.Error) {
@ -92,10 +96,10 @@ func (a *accountDB) UpdateAccount(ctx context.Context, account *gtsmodel.Account
WherePK() WherePK()
_, err := q.Exec(ctx) _, err := q.Exec(ctx)
if err != nil {
err = processErrorResponse(err) return nil, a.conn.ProcessError(err)
}
return account, err return account, nil
} }
func (a *accountDB) GetInstanceAccount(ctx context.Context, domain string) (*gtsmodel.Account, db.Error) { func (a *accountDB) GetInstanceAccount(ctx context.Context, domain string) (*gtsmodel.Account, db.Error) {
@ -113,9 +117,11 @@ func (a *accountDB) GetInstanceAccount(ctx context.Context, domain string) (*gts
WhereGroup(" AND ", whereEmptyOrNull("domain")) WhereGroup(" AND ", whereEmptyOrNull("domain"))
} }
err := processErrorResponse(q.Scan(ctx)) err := q.Scan(ctx)
if err != nil {
return account, err return nil, a.conn.ProcessError(err)
}
return account, nil
} }
func (a *accountDB) GetAccountLastPosted(ctx context.Context, accountID string) (time.Time, db.Error) { func (a *accountDB) GetAccountLastPosted(ctx context.Context, accountID string) (time.Time, db.Error) {
@ -129,9 +135,11 @@ func (a *accountDB) GetAccountLastPosted(ctx context.Context, accountID string)
Where("account_id = ?", accountID). Where("account_id = ?", accountID).
Column("created_at") Column("created_at")
err := processErrorResponse(q.Scan(ctx)) err := q.Scan(ctx)
if err != nil {
return status.CreatedAt, err return time.Time{}, a.conn.ProcessError(err)
}
return status.CreatedAt, nil
} }
func (a *accountDB) SetAccountHeaderOrAvatar(ctx context.Context, mediaAttachment *gtsmodel.MediaAttachment, accountID string) db.Error { func (a *accountDB) SetAccountHeaderOrAvatar(ctx context.Context, mediaAttachment *gtsmodel.MediaAttachment, accountID string) db.Error {
@ -153,17 +161,17 @@ func (a *accountDB) SetAccountHeaderOrAvatar(ctx context.Context, mediaAttachmen
NewInsert(). NewInsert().
Model(mediaAttachment). Model(mediaAttachment).
Exec(ctx); err != nil { Exec(ctx); err != nil {
return err return a.conn.ProcessError(err)
} }
if _, err := a.conn. if _, err := a.conn.
NewUpdate(). NewUpdate().
Model(&gtsmodel.Account{}). Model(&gtsmodel.Account{}).
Set(fmt.Sprintf("%s_media_attachment_id = ?", headerOrAVI), mediaAttachment.ID). Set(fmt.Sprintf("%s_media_attachment_id = ?", headerOrAVI), mediaAttachment.ID).
Where("id = ?", accountID). Where("id = ?", accountID).
Exec(ctx); err != nil { Exec(ctx); err != nil {
return err return a.conn.ProcessError(err)
} }
return nil return nil
} }
@ -174,9 +182,11 @@ func (a *accountDB) GetLocalAccountByUsername(ctx context.Context, username stri
Where("username = ?", username). Where("username = ?", username).
WhereGroup(" AND ", whereEmptyOrNull("domain")) WhereGroup(" AND ", whereEmptyOrNull("domain"))
err := processErrorResponse(q.Scan(ctx)) err := q.Scan(ctx)
if err != nil {
return account, err return nil, a.conn.ProcessError(err)
}
return account, nil
} }
func (a *accountDB) GetAccountFaves(ctx context.Context, accountID string) ([]*gtsmodel.StatusFave, db.Error) { func (a *accountDB) GetAccountFaves(ctx context.Context, accountID string) ([]*gtsmodel.StatusFave, db.Error) {
@ -187,8 +197,9 @@ func (a *accountDB) GetAccountFaves(ctx context.Context, accountID string) ([]*g
Model(faves). Model(faves).
Where("account_id = ?", accountID). Where("account_id = ?", accountID).
Scan(ctx); err != nil { Scan(ctx); err != nil {
return nil, err return nil, a.conn.ProcessError(err)
} }
return *faves, nil return *faves, nil
} }
@ -201,7 +212,6 @@ func (a *accountDB) CountAccountStatuses(ctx context.Context, accountID string)
} }
func (a *accountDB) GetAccountStatuses(ctx context.Context, accountID string, limit int, excludeReplies bool, maxID string, pinnedOnly bool, mediaOnly bool) ([]*gtsmodel.Status, db.Error) { func (a *accountDB) GetAccountStatuses(ctx context.Context, accountID string, limit int, excludeReplies bool, maxID string, pinnedOnly bool, mediaOnly bool) ([]*gtsmodel.Status, db.Error) {
a.log.Debugf("getting statuses for account %s", accountID)
statuses := []*gtsmodel.Status{} statuses := []*gtsmodel.Status{}
q := a.conn. q := a.conn.
@ -238,14 +248,13 @@ func (a *accountDB) GetAccountStatuses(ctx context.Context, accountID string, li
} }
if err := q.Scan(ctx); err != nil { if err := q.Scan(ctx); err != nil {
return nil, err return nil, a.conn.ProcessError(err)
} }
if len(statuses) == 0 { if len(statuses) == 0 {
return nil, db.ErrNoEntries return nil, db.ErrNoEntries
} }
a.log.Debugf("returning statuses for account %s", accountID)
return statuses, nil return statuses, nil
} }
@ -273,7 +282,7 @@ func (a *accountDB) GetAccountBlocks(ctx context.Context, accountID string, maxI
err := fq.Scan(ctx) err := fq.Scan(ctx)
if err != nil { if err != nil {
return nil, "", "", err return nil, "", "", a.conn.ProcessError(err)
} }
if len(blocks) == 0 { if len(blocks) == 0 {

View file

@ -29,20 +29,17 @@ import (
"strings" "strings"
"time" "time"
"github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/id" "github.com/superseriousbusiness/gotosocial/internal/id"
"github.com/superseriousbusiness/gotosocial/internal/util" "github.com/superseriousbusiness/gotosocial/internal/util"
"github.com/uptrace/bun"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
) )
type adminDB struct { type adminDB struct {
config *config.Config config *config.Config
conn *bun.DB conn *DBConn
log *logrus.Logger
} }
func (a *adminDB) IsUsernameAvailable(ctx context.Context, username string) (bool, db.Error) { func (a *adminDB) IsUsernameAvailable(ctx context.Context, username string) (bool, db.Error) {
@ -52,7 +49,7 @@ func (a *adminDB) IsUsernameAvailable(ctx context.Context, username string) (boo
Where("username = ?", username). Where("username = ?", username).
Where("domain = ?", nil) Where("domain = ?", nil)
return notExists(ctx, q) return a.conn.NotExists(ctx, q)
} }
func (a *adminDB) IsEmailAvailable(ctx context.Context, email string) (bool, db.Error) { func (a *adminDB) IsEmailAvailable(ctx context.Context, email string) (bool, db.Error) {
@ -72,7 +69,7 @@ func (a *adminDB) IsEmailAvailable(ctx context.Context, email string) (bool, db.
// fail because we found something // fail because we found something
return false, fmt.Errorf("email domain %s is blocked", domain) return false, fmt.Errorf("email domain %s is blocked", domain)
} else if err != sql.ErrNoRows { } else if err != sql.ErrNoRows {
return false, processErrorResponse(err) return false, a.conn.ProcessError(err)
} }
// check if this email is associated with a user already // check if this email is associated with a user already
@ -82,13 +79,13 @@ func (a *adminDB) IsEmailAvailable(ctx context.Context, email string) (bool, db.
Where("email = ?", email). Where("email = ?", email).
WhereOr("unconfirmed_email = ?", email) WhereOr("unconfirmed_email = ?", email)
return notExists(ctx, q) return a.conn.NotExists(ctx, q)
} }
func (a *adminDB) NewSignup(ctx context.Context, username string, reason string, requireApproval bool, email string, password string, signUpIP net.IP, locale string, appID string, emailVerified bool, admin bool) (*gtsmodel.User, db.Error) { func (a *adminDB) NewSignup(ctx context.Context, username string, reason string, requireApproval bool, email string, password string, signUpIP net.IP, locale string, appID string, emailVerified bool, admin bool) (*gtsmodel.User, db.Error) {
key, err := rsa.GenerateKey(rand.Reader, 2048) key, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil { if err != nil {
a.log.Errorf("error creating new rsa key: %s", err) a.conn.log.Errorf("error creating new rsa key: %s", err)
return nil, err return nil, err
} }
@ -128,7 +125,7 @@ func (a *adminDB) NewSignup(ctx context.Context, username string, reason string,
NewInsert(). NewInsert().
Model(acct). Model(acct).
Exec(ctx); err != nil { Exec(ctx); err != nil {
return nil, err return nil, a.conn.ProcessError(err)
} }
} }
@ -167,7 +164,7 @@ func (a *adminDB) NewSignup(ctx context.Context, username string, reason string,
NewInsert(). NewInsert().
Model(u). Model(u).
Exec(ctx); err != nil { Exec(ctx); err != nil {
return nil, err return nil, a.conn.ProcessError(err)
} }
return u, nil return u, nil
@ -184,15 +181,15 @@ func (a *adminDB) CreateInstanceAccount(ctx context.Context) db.Error {
WhereGroup(" AND ", whereEmptyOrNull("domain")) WhereGroup(" AND ", whereEmptyOrNull("domain"))
count, err := existsQ.Count(ctx) count, err := existsQ.Count(ctx)
if err != nil && count == 1 { if err != nil && count == 1 {
a.log.Infof("instance account %s already exists", username) a.conn.log.Infof("instance account %s already exists", username)
return nil return nil
} else if err != sql.ErrNoRows { } else if err != sql.ErrNoRows {
return processErrorResponse(err) return a.conn.ProcessError(err)
} }
key, err := rsa.GenerateKey(rand.Reader, 2048) key, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil { if err != nil {
a.log.Errorf("error creating new rsa key: %s", err) a.conn.log.Errorf("error creating new rsa key: %s", err)
return err return err
} }
@ -224,10 +221,10 @@ func (a *adminDB) CreateInstanceAccount(ctx context.Context) db.Error {
Model(acct) Model(acct)
if _, err := insertQ.Exec(ctx); err != nil { if _, err := insertQ.Exec(ctx); err != nil {
return err return a.conn.ProcessError(err)
} }
a.log.Infof("instance account %s CREATED with id %s", username, acct.ID) a.conn.log.Infof("instance account %s CREATED with id %s", username, acct.ID)
return nil return nil
} }
@ -240,12 +237,12 @@ func (a *adminDB) CreateInstanceInstance(ctx context.Context) db.Error {
Model(&gtsmodel.Instance{}). Model(&gtsmodel.Instance{}).
Where("domain = ?", domain) Where("domain = ?", domain)
exists, err := exists(ctx, q) exists, err := a.conn.Exists(ctx, q)
if err != nil { if err != nil {
return err return err
} }
if exists { if exists {
a.log.Infof("instance entry already exists") a.conn.log.Infof("instance entry already exists")
return nil return nil
} }
@ -266,10 +263,10 @@ func (a *adminDB) CreateInstanceInstance(ctx context.Context) db.Error {
Model(i) Model(i)
_, err = insertQ.Exec(ctx) _, err = insertQ.Exec(ctx)
err = processErrorResponse(err) if err != nil {
return a.conn.ProcessError(err)
}
if err == nil { a.conn.log.Infof("created instance instance %s with id %s", domain, i.ID)
a.log.Infof("created instance instance %s with id %s", domain, i.ID) return nil
}
return err
} }

View file

@ -21,9 +21,7 @@ package bundb
import ( import (
"context" "context"
"errors" "errors"
"strings"
"github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/uptrace/bun" "github.com/uptrace/bun"
@ -31,16 +29,12 @@ import (
type basicDB struct { type basicDB struct {
config *config.Config config *config.Config
conn *bun.DB conn *DBConn
log *logrus.Logger
} }
func (b *basicDB) Put(ctx context.Context, i interface{}) db.Error { func (b *basicDB) Put(ctx context.Context, i interface{}) db.Error {
_, err := b.conn.NewInsert().Model(i).Exec(ctx) _, err := b.conn.NewInsert().Model(i).Exec(ctx)
if err != nil && strings.Contains(err.Error(), "duplicate key value violates unique constraint") { return b.conn.ProcessError(err)
return db.ErrAlreadyExists
}
return err
} }
func (b *basicDB) GetByID(ctx context.Context, id string, i interface{}) db.Error { func (b *basicDB) GetByID(ctx context.Context, id string, i interface{}) db.Error {
@ -49,7 +43,8 @@ func (b *basicDB) GetByID(ctx context.Context, id string, i interface{}) db.Erro
Model(i). Model(i).
Where("id = ?", id) Where("id = ?", id)
return processErrorResponse(q.Scan(ctx)) err := q.Scan(ctx)
return b.conn.ProcessError(err)
} }
func (b *basicDB) GetWhere(ctx context.Context, where []db.Where, i interface{}) db.Error { func (b *basicDB) GetWhere(ctx context.Context, where []db.Where, i interface{}) db.Error {
@ -59,7 +54,6 @@ func (b *basicDB) GetWhere(ctx context.Context, where []db.Where, i interface{})
q := b.conn.NewSelect().Model(i) q := b.conn.NewSelect().Model(i)
for _, w := range where { for _, w := range where {
if w.Value == nil { if w.Value == nil {
q = q.Where("? IS NULL", bun.Ident(w.Key)) q = q.Where("? IS NULL", bun.Ident(w.Key))
} else { } else {
@ -71,7 +65,8 @@ func (b *basicDB) GetWhere(ctx context.Context, where []db.Where, i interface{})
} }
} }
return processErrorResponse(q.Scan(ctx)) err := q.Scan(ctx)
return b.conn.ProcessError(err)
} }
func (b *basicDB) GetAll(ctx context.Context, i interface{}) db.Error { func (b *basicDB) GetAll(ctx context.Context, i interface{}) db.Error {
@ -79,7 +74,8 @@ func (b *basicDB) GetAll(ctx context.Context, i interface{}) db.Error {
NewSelect(). NewSelect().
Model(i) Model(i)
return processErrorResponse(q.Scan(ctx)) err := q.Scan(ctx)
return b.conn.ProcessError(err)
} }
func (b *basicDB) DeleteByID(ctx context.Context, id string, i interface{}) db.Error { func (b *basicDB) DeleteByID(ctx context.Context, id string, i interface{}) db.Error {
@ -89,8 +85,7 @@ func (b *basicDB) DeleteByID(ctx context.Context, id string, i interface{}) db.E
Where("id = ?", id) Where("id = ?", id)
_, err := q.Exec(ctx) _, err := q.Exec(ctx)
return b.conn.ProcessError(err)
return processErrorResponse(err)
} }
func (b *basicDB) DeleteWhere(ctx context.Context, where []db.Where, i interface{}) db.Error { func (b *basicDB) DeleteWhere(ctx context.Context, where []db.Where, i interface{}) db.Error {
@ -107,8 +102,7 @@ func (b *basicDB) DeleteWhere(ctx context.Context, where []db.Where, i interface
} }
_, err := q.Exec(ctx) _, err := q.Exec(ctx)
return b.conn.ProcessError(err)
return processErrorResponse(err)
} }
func (b *basicDB) UpdateByID(ctx context.Context, id string, i interface{}) db.Error { func (b *basicDB) UpdateByID(ctx context.Context, id string, i interface{}) db.Error {
@ -118,8 +112,7 @@ func (b *basicDB) UpdateByID(ctx context.Context, id string, i interface{}) db.E
WherePK() WherePK()
_, err := q.Exec(ctx) _, err := q.Exec(ctx)
return b.conn.ProcessError(err)
return processErrorResponse(err)
} }
func (b *basicDB) UpdateOneByID(ctx context.Context, id string, key string, value interface{}, i interface{}) db.Error { func (b *basicDB) UpdateOneByID(ctx context.Context, id string, key string, value interface{}, i interface{}) db.Error {
@ -129,8 +122,7 @@ func (b *basicDB) UpdateOneByID(ctx context.Context, id string, key string, valu
WherePK() WherePK()
_, err := q.Exec(ctx) _, err := q.Exec(ctx)
return b.conn.ProcessError(err)
return processErrorResponse(err)
} }
func (b *basicDB) UpdateWhere(ctx context.Context, where []db.Where, key string, value interface{}, i interface{}) db.Error { func (b *basicDB) UpdateWhere(ctx context.Context, where []db.Where, key string, value interface{}, i interface{}) db.Error {
@ -151,8 +143,7 @@ func (b *basicDB) UpdateWhere(ctx context.Context, where []db.Where, key string,
q = q.Set("? = ?", bun.Safe(key), value) q = q.Set("? = ?", bun.Safe(key), value)
_, err := q.Exec(ctx) _, err := q.Exec(ctx)
return b.conn.ProcessError(err)
return processErrorResponse(err)
} }
func (b *basicDB) CreateTable(ctx context.Context, i interface{}) db.Error { func (b *basicDB) CreateTable(ctx context.Context, i interface{}) db.Error {
@ -162,7 +153,7 @@ func (b *basicDB) CreateTable(ctx context.Context, i interface{}) db.Error {
func (b *basicDB) DropTable(ctx context.Context, i interface{}) db.Error { func (b *basicDB) DropTable(ctx context.Context, i interface{}) db.Error {
_, err := b.conn.NewDropTable().Model(i).IfExists().Exec(ctx) _, err := b.conn.NewDropTable().Model(i).IfExists().Exec(ctx)
return processErrorResponse(err) return b.conn.ProcessError(err)
} }
func (b *basicDB) IsHealthy(ctx context.Context) db.Error { func (b *basicDB) IsHealthy(ctx context.Context) db.Error {
@ -170,10 +161,6 @@ func (b *basicDB) IsHealthy(ctx context.Context) db.Error {
} }
func (b *basicDB) Stop(ctx context.Context) db.Error { func (b *basicDB) Stop(ctx context.Context) db.Error {
b.log.Info("closing db connection") b.conn.log.Info("closing db connection")
if err := b.conn.Close(); err != nil { return b.conn.Close()
// only cancel if there's a problem closing the db
return err
}
return nil
} }

View file

@ -30,15 +30,19 @@ import (
"strings" "strings"
"time" "time"
"github.com/ReneKroon/ttlcache"
"github.com/jackc/pgx/v4" "github.com/jackc/pgx/v4"
"github.com/jackc/pgx/v4/stdlib" "github.com/jackc/pgx/v4/stdlib"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/cache"
"github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/id" "github.com/superseriousbusiness/gotosocial/internal/id"
"github.com/uptrace/bun" "github.com/uptrace/bun"
"github.com/uptrace/bun/dialect/pgdialect" "github.com/uptrace/bun/dialect/pgdialect"
"github.com/uptrace/bun/dialect/sqlitedialect"
_ "modernc.org/sqlite"
) )
const ( const (
@ -66,15 +70,14 @@ type bunDBService struct {
db.Status db.Status
db.Timeline db.Timeline
config *config.Config config *config.Config
conn *bun.DB conn *DBConn
log *logrus.Logger
} }
// NewBunDBService returns a bunDB derived from the provided config, which implements the go-fed DB interface. // NewBunDBService returns a bunDB derived from the provided config, which implements the go-fed DB interface.
// Under the hood, it uses https://github.com/uptrace/bun to create and maintain a database connection. // Under the hood, it uses https://github.com/uptrace/bun to create and maintain a database connection.
func NewBunDBService(ctx context.Context, c *config.Config, log *logrus.Logger) (db.DB, error) { func NewBunDBService(ctx context.Context, c *config.Config, log *logrus.Logger) (db.DB, error) {
var sqldb *sql.DB var sqldb *sql.DB
var conn *bun.DB var conn *DBConn
// depending on the database type we're trying to create, we need to use a different driver... // depending on the database type we're trying to create, we need to use a different driver...
switch strings.ToLower(c.DBConfig.Type) { switch strings.ToLower(c.DBConfig.Type) {
@ -85,10 +88,24 @@ func NewBunDBService(ctx context.Context, c *config.Config, log *logrus.Logger)
return nil, fmt.Errorf("could not create bundb postgres options: %s", err) return nil, fmt.Errorf("could not create bundb postgres options: %s", err)
} }
sqldb = stdlib.OpenDB(*opts) sqldb = stdlib.OpenDB(*opts)
conn = bun.NewDB(sqldb, pgdialect.New()) conn = WrapDBConn(bun.NewDB(sqldb, pgdialect.New()), log)
case dbTypeSqlite: case dbTypeSqlite:
// SQLITE // SQLITE
// TODO: https://bun.uptrace.dev/guide/drivers.html#sqlite var err error
sqldb, err = sql.Open("sqlite", c.DBConfig.Address)
if err != nil {
return nil, fmt.Errorf("could not open sqlite db: %s", err)
}
conn = WrapDBConn(bun.NewDB(sqldb, sqlitedialect.New()), log)
if strings.HasPrefix(strings.TrimPrefix(c.DBConfig.Address, "file:"), ":memory:") {
log.Warn("sqlite in-memory database should only be used for debugging")
// don't close connections on disconnect -- otherwise
// the SQLite database will be deleted when there
// are no active connections
sqldb.SetConnMaxLifetime(0)
}
default: default:
return nil, fmt.Errorf("database type %s not supported for bundb", strings.ToLower(c.DBConfig.Type)) return nil, fmt.Errorf("database type %s not supported for bundb", strings.ToLower(c.DBConfig.Type))
} }
@ -108,66 +125,56 @@ func NewBunDBService(ctx context.Context, c *config.Config, log *logrus.Logger)
Account: &accountDB{ Account: &accountDB{
config: c, config: c,
conn: conn, conn: conn,
log: log,
}, },
Admin: &adminDB{ Admin: &adminDB{
config: c, config: c,
conn: conn, conn: conn,
log: log,
}, },
Basic: &basicDB{ Basic: &basicDB{
config: c, config: c,
conn: conn, conn: conn,
log: log,
}, },
Domain: &domainDB{ Domain: &domainDB{
config: c, config: c,
conn: conn, conn: conn,
log: log,
}, },
Instance: &instanceDB{ Instance: &instanceDB{
config: c, config: c,
conn: conn, conn: conn,
log: log,
}, },
Media: &mediaDB{ Media: &mediaDB{
config: c, config: c,
conn: conn, conn: conn,
log: log,
}, },
Mention: &mentionDB{ Mention: &mentionDB{
config: c, config: c,
conn: conn, conn: conn,
log: log, cache: ttlcache.NewCache(),
}, },
Notification: &notificationDB{ Notification: &notificationDB{
config: c, config: c,
conn: conn, conn: conn,
log: log, cache: ttlcache.NewCache(),
}, },
Relationship: &relationshipDB{ Relationship: &relationshipDB{
config: c, config: c,
conn: conn, conn: conn,
log: log,
}, },
Session: &sessionDB{ Session: &sessionDB{
config: c, config: c,
conn: conn, conn: conn,
log: log,
}, },
Status: &statusDB{ Status: &statusDB{
config: c, config: c,
conn: conn, conn: conn,
log: log, cache: cache.NewStatusCache(),
}, },
Timeline: &timelineDB{ Timeline: &timelineDB{
config: c, config: c,
conn: conn, conn: conn,
log: log,
}, },
config: c, config: c,
conn: conn, conn: conn,
log: log,
} }
// we can confidently return this useable service now // we can confidently return this useable service now
@ -332,7 +339,7 @@ func (ps *bunDBService) MentionStringsToMentions(ctx context.Context, targetAcco
if err != nil { if err != nil {
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
// no result found for this username/domain so just don't include it as a mencho and carry on about our business // no result found for this username/domain so just don't include it as a mencho and carry on about our business
ps.log.Debugf("no account found with username '%s' and domain '%s', skipping it", username, domain) ps.conn.log.Debugf("no account found with username '%s' and domain '%s', skipping it", username, domain)
continue continue
} }
// a serious error has happened so bail // a serious error has happened so bail
@ -398,7 +405,7 @@ func (ps *bunDBService) EmojiStringsToEmojis(ctx context.Context, emojis []strin
if err != nil { if err != nil {
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
// no result found for this username/domain so just don't include it as an emoji and carry on about our business // no result found for this username/domain so just don't include it as an emoji and carry on about our business
ps.log.Debugf("no emoji found with shortcode %s, skipping it", e) ps.conn.log.Debugf("no emoji found with shortcode %s, skipping it", e)
continue continue
} }
// a serious error has happened so bail // a serious error has happened so bail

72
internal/db/bundb/conn.go Normal file
View file

@ -0,0 +1,72 @@
package bundb
import (
"context"
"database/sql"
"github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/uptrace/bun"
"github.com/uptrace/bun/dialect"
)
// dbConn wrapps a bun.DB conn to provide SQL-type specific additional functionality
type DBConn struct {
errProc func(error) db.Error // errProc is the SQL-type specific error processor
log *logrus.Logger // log is the logger passed with this DBConn
*bun.DB // DB is the underlying bun.DB connection
}
// WrapDBConn @TODO
func WrapDBConn(dbConn *bun.DB, log *logrus.Logger) *DBConn {
var errProc func(error) db.Error
switch dbConn.Dialect().Name() {
case dialect.PG:
errProc = processPostgresError
case dialect.SQLite:
errProc = processSQLiteError
default:
panic("unknown dialect name: " + dbConn.Dialect().Name().String())
}
return &DBConn{
errProc: errProc,
log: log,
DB: dbConn,
}
}
// ProcessError processes an error to replace any known values with our own db.Error types,
// making it easier to catch specific situations (e.g. no rows, already exists, etc)
func (conn *DBConn) ProcessError(err error) db.Error {
switch {
case err == nil:
return nil
case err == sql.ErrNoRows:
return db.ErrNoEntries
default:
return conn.errProc(err)
}
}
// Exists checks the results of a SelectQuery for the existence of the data in question, masking ErrNoEntries errors
func (conn *DBConn) Exists(ctx context.Context, query *bun.SelectQuery) (bool, db.Error) {
// Get the select query result
count, err := query.Count(ctx)
// Process error as our own and check if it exists
switch err := conn.ProcessError(err); err {
case nil:
return (count != 0), nil
case db.ErrNoEntries:
return false, nil
default:
return false, err
}
}
// NotExists is the functional opposite of conn.Exists()
func (conn *DBConn) NotExists(ctx context.Context, query *bun.SelectQuery) (bool, db.Error) {
// Simply inverse of conn.exists()
exists, err := conn.Exists(ctx, query)
return !exists, err
}

View file

@ -22,18 +22,15 @@ import (
"context" "context"
"net/url" "net/url"
"github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/util" "github.com/superseriousbusiness/gotosocial/internal/util"
"github.com/uptrace/bun"
) )
type domainDB struct { type domainDB struct {
config *config.Config config *config.Config
conn *bun.DB conn *DBConn
log *logrus.Logger
} }
func (d *domainDB) IsDomainBlocked(ctx context.Context, domain string) (bool, db.Error) { func (d *domainDB) IsDomainBlocked(ctx context.Context, domain string) (bool, db.Error) {
@ -47,7 +44,7 @@ func (d *domainDB) IsDomainBlocked(ctx context.Context, domain string) (bool, db
Where("LOWER(domain) = LOWER(?)", domain). Where("LOWER(domain) = LOWER(?)", domain).
Limit(1) Limit(1)
return exists(ctx, q) return d.conn.Exists(ctx, q)
} }
func (d *domainDB) AreDomainsBlocked(ctx context.Context, domains []string) (bool, db.Error) { func (d *domainDB) AreDomainsBlocked(ctx context.Context, domains []string) (bool, db.Error) {

View file

@ -0,0 +1,43 @@
package bundb
import (
"github.com/jackc/pgconn"
"github.com/superseriousbusiness/gotosocial/internal/db"
"modernc.org/sqlite"
sqlite3 "modernc.org/sqlite/lib"
)
// processPostgresError processes an error, replacing any postgres specific errors with our own error type
func processPostgresError(err error) db.Error {
// Attempt to cast as postgres
pgErr, ok := err.(*pgconn.PgError)
if !ok {
return err
}
// Handle supplied error code:
// (https://www.postgresql.org/docs/10/errcodes-appendix.html)
switch pgErr.Code {
case "23505" /* unique_violation */ :
return db.ErrAlreadyExists
default:
return err
}
}
// processSQLiteError processes an error, replacing any sqlite specific errors with our own error type
func processSQLiteError(err error) db.Error {
// Attempt to cast as sqlite
sqliteErr, ok := err.(*sqlite.Error)
if !ok {
return err
}
// Handle supplied error code:
switch sqliteErr.Code() {
case sqlite3.SQLITE_CONSTRAINT_UNIQUE:
return db.ErrAlreadyExists
default:
return err
}
}

View file

@ -21,7 +21,6 @@ package bundb
import ( import (
"context" "context"
"github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
@ -30,8 +29,7 @@ import (
type instanceDB struct { type instanceDB struct {
config *config.Config config *config.Config
conn *bun.DB conn *DBConn
log *logrus.Logger
} }
func (i *instanceDB) CountInstanceUsers(ctx context.Context, domain string) (int, db.Error) { func (i *instanceDB) CountInstanceUsers(ctx context.Context, domain string) (int, db.Error) {
@ -49,8 +47,10 @@ func (i *instanceDB) CountInstanceUsers(ctx context.Context, domain string) (int
} }
count, err := q.Count(ctx) count, err := q.Count(ctx)
if err != nil {
return count, processErrorResponse(err) return 0, i.conn.ProcessError(err)
}
return count, nil
} }
func (i *instanceDB) CountInstanceStatuses(ctx context.Context, domain string) (int, db.Error) { func (i *instanceDB) CountInstanceStatuses(ctx context.Context, domain string) (int, db.Error) {
@ -68,8 +68,10 @@ func (i *instanceDB) CountInstanceStatuses(ctx context.Context, domain string) (
} }
count, err := q.Count(ctx) count, err := q.Count(ctx)
if err != nil {
return count, processErrorResponse(err) return 0, i.conn.ProcessError(err)
}
return count, nil
} }
func (i *instanceDB) CountInstanceDomains(ctx context.Context, domain string) (int, db.Error) { func (i *instanceDB) CountInstanceDomains(ctx context.Context, domain string) (int, db.Error) {
@ -89,12 +91,14 @@ func (i *instanceDB) CountInstanceDomains(ctx context.Context, domain string) (i
} }
count, err := q.Count(ctx) count, err := q.Count(ctx)
if err != nil {
return count, processErrorResponse(err) return 0, i.conn.ProcessError(err)
}
return count, nil
} }
func (i *instanceDB) GetInstanceAccounts(ctx context.Context, domain string, maxID string, limit int) ([]*gtsmodel.Account, db.Error) { func (i *instanceDB) GetInstanceAccounts(ctx context.Context, domain string, maxID string, limit int) ([]*gtsmodel.Account, db.Error) {
i.log.Debug("GetAccountsForInstance") i.conn.log.Debug("GetAccountsForInstance")
accounts := []*gtsmodel.Account{} accounts := []*gtsmodel.Account{}
@ -111,7 +115,9 @@ func (i *instanceDB) GetInstanceAccounts(ctx context.Context, domain string, max
q = q.Limit(limit) q = q.Limit(limit)
} }
err := processErrorResponse(q.Scan(ctx)) err := q.Scan(ctx)
if err != nil {
return accounts, err return nil, i.conn.ProcessError(err)
}
return accounts, nil
} }

View file

@ -21,7 +21,6 @@ package bundb
import ( import (
"context" "context"
"github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
@ -30,8 +29,7 @@ import (
type mediaDB struct { type mediaDB struct {
config *config.Config config *config.Config
conn *bun.DB conn *DBConn
log *logrus.Logger
} }
func (m *mediaDB) newMediaQ(i interface{}) *bun.SelectQuery { func (m *mediaDB) newMediaQ(i interface{}) *bun.SelectQuery {
@ -47,7 +45,9 @@ func (m *mediaDB) GetAttachmentByID(ctx context.Context, id string) (*gtsmodel.M
q := m.newMediaQ(attachment). q := m.newMediaQ(attachment).
Where("media_attachment.id = ?", id) Where("media_attachment.id = ?", id)
err := processErrorResponse(q.Scan(ctx)) err := q.Scan(ctx)
if err != nil {
return attachment, err return nil, m.conn.ProcessError(err)
}
return attachment, nil
} }

View file

@ -21,8 +21,7 @@ package bundb
import ( import (
"context" "context"
"github.com/sirupsen/logrus" "github.com/ReneKroon/ttlcache"
"github.com/superseriousbusiness/gotosocial/internal/cache"
"github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
@ -31,38 +30,8 @@ import (
type mentionDB struct { type mentionDB struct {
config *config.Config config *config.Config
conn *bun.DB conn *DBConn
log *logrus.Logger cache *ttlcache.Cache
cache cache.Cache
}
func (m *mentionDB) cacheMention(id string, mention *gtsmodel.Mention) {
if m.cache == nil {
m.cache = cache.New()
}
if err := m.cache.Store(id, mention); err != nil {
m.log.Panicf("mentionDB: error storing in cache: %s", err)
}
}
func (m *mentionDB) mentionCached(id string) (*gtsmodel.Mention, bool) {
if m.cache == nil {
m.cache = cache.New()
return nil, false
}
mI, err := m.cache.Fetch(id)
if err != nil || mI == nil {
return nil, false
}
mention, ok := mI.(*gtsmodel.Mention)
if !ok {
m.log.Panicf("mentionDB: cached interface with key %s was not a mention", id)
}
return mention, true
} }
func (m *mentionDB) newMentionQ(i interface{}) *bun.SelectQuery { func (m *mentionDB) newMentionQ(i interface{}) *bun.SelectQuery {
@ -74,33 +43,57 @@ func (m *mentionDB) newMentionQ(i interface{}) *bun.SelectQuery {
Relation("TargetAccount") Relation("TargetAccount")
} }
func (m *mentionDB) GetMention(ctx context.Context, id string) (*gtsmodel.Mention, db.Error) { func (m *mentionDB) getMentionCached(id string) (*gtsmodel.Mention, bool) {
if mention, cached := m.mentionCached(id); cached { v, ok := m.cache.Get(id)
return mention, nil if !ok {
return nil, false
}
return v.(*gtsmodel.Mention), true
} }
func (m *mentionDB) putMentionCache(mention *gtsmodel.Mention) {
m.cache.Set(mention.ID, mention)
}
func (m *mentionDB) getMentionDB(ctx context.Context, id string) (*gtsmodel.Mention, db.Error) {
mention := &gtsmodel.Mention{} mention := &gtsmodel.Mention{}
q := m.newMentionQ(mention). q := m.newMentionQ(mention).
Where("mention.id = ?", id) Where("mention.id = ?", id)
err := processErrorResponse(q.Scan(ctx)) err := q.Scan(ctx)
if err != nil {
if err == nil && mention != nil { return nil, m.conn.ProcessError(err)
m.cacheMention(id, mention)
} }
return mention, err m.putMentionCache(mention)
return mention, nil
}
func (m *mentionDB) GetMention(ctx context.Context, id string) (*gtsmodel.Mention, db.Error) {
if mention, cached := m.getMentionCached(id); cached {
return mention, nil
}
return m.getMentionDB(ctx, id)
} }
func (m *mentionDB) GetMentions(ctx context.Context, ids []string) ([]*gtsmodel.Mention, db.Error) { func (m *mentionDB) GetMentions(ctx context.Context, ids []string) ([]*gtsmodel.Mention, db.Error) {
mentions := []*gtsmodel.Mention{} mentions := make([]*gtsmodel.Mention, 0, len(ids))
for _, i := range ids { for _, id := range ids {
mention, err := m.GetMention(ctx, i) // Attempt fetch from cache
if err != nil { mention, cached := m.getMentionCached(id)
return nil, processErrorResponse(err) if cached {
mentions = append(mentions, mention)
} }
// Attempt fetch from DB
mention, err := m.getMentionDB(ctx, id)
if err != nil {
return nil, err
}
// Append mention
mentions = append(mentions, mention) mentions = append(mentions, mention)
} }

View file

@ -21,8 +21,7 @@ package bundb
import ( import (
"context" "context"
"github.com/sirupsen/logrus" "github.com/ReneKroon/ttlcache"
"github.com/superseriousbusiness/gotosocial/internal/cache"
"github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
@ -31,38 +30,8 @@ import (
type notificationDB struct { type notificationDB struct {
config *config.Config config *config.Config
conn *bun.DB conn *DBConn
log *logrus.Logger cache *ttlcache.Cache
cache cache.Cache
}
func (n *notificationDB) cacheNotification(id string, notification *gtsmodel.Notification) {
if n.cache == nil {
n.cache = cache.New()
}
if err := n.cache.Store(id, notification); err != nil {
n.log.Panicf("notificationDB: error storing in cache: %s", err)
}
}
func (n *notificationDB) notificationCached(id string) (*gtsmodel.Notification, bool) {
if n.cache == nil {
n.cache = cache.New()
return nil, false
}
nI, err := n.cache.Fetch(id)
if err != nil || nI == nil {
return nil, false
}
notification, ok := nI.(*gtsmodel.Notification)
if !ok {
n.log.Panicf("notificationDB: cached interface with key %s was not a notification", id)
}
return notification, true
} }
func (n *notificationDB) newNotificationQ(i interface{}) *bun.SelectQuery { func (n *notificationDB) newNotificationQ(i interface{}) *bun.SelectQuery {
@ -75,30 +44,30 @@ func (n *notificationDB) newNotificationQ(i interface{}) *bun.SelectQuery {
} }
func (n *notificationDB) GetNotification(ctx context.Context, id string) (*gtsmodel.Notification, db.Error) { func (n *notificationDB) GetNotification(ctx context.Context, id string) (*gtsmodel.Notification, db.Error) {
if notification, cached := n.notificationCached(id); cached { if notification, cached := n.getNotificationCache(id); cached {
return notification, nil return notification, nil
} }
notification := &gtsmodel.Notification{} notif := &gtsmodel.Notification{}
err := n.getNotificationDB(ctx, id, notif)
q := n.newNotificationQ(notification). if err != nil {
Where("notification.id = ?", id) return nil, err
err := processErrorResponse(q.Scan(ctx))
if err == nil && notification != nil {
n.cacheNotification(id, notification)
} }
return notif, nil
return notification, err
} }
func (n *notificationDB) GetNotifications(ctx context.Context, accountID string, limit int, maxID string, sinceID string) ([]*gtsmodel.Notification, db.Error) { func (n *notificationDB) GetNotifications(ctx context.Context, accountID string, limit int, maxID string, sinceID string) ([]*gtsmodel.Notification, db.Error) {
// begin by selecting just the IDs // Ensure reasonable
notifIDs := []*gtsmodel.Notification{} if limit < 0 {
limit = 0
}
// Make a guess for slice size
notifications := make([]*gtsmodel.Notification, 0, limit)
q := n.conn. q := n.conn.
NewSelect(). NewSelect().
Model(&notifIDs). Model(&notifications).
Column("id"). Column("id").
Where("target_account_id = ?", accountID). Where("target_account_id = ?", accountID).
Order("id DESC") Order("id DESC")
@ -115,22 +84,52 @@ func (n *notificationDB) GetNotifications(ctx context.Context, accountID string,
q = q.Limit(limit) q = q.Limit(limit)
} }
err := processErrorResponse(q.Scan(ctx)) err := q.Scan(ctx)
if err != nil { if err != nil {
return nil, err return nil, n.conn.ProcessError(err)
} }
// now we have the IDs, select the notifs one by one // now we have the IDs, select the notifs one by one
// reason for this is that for each notif, we can instead get it from our cache if it's cached // reason for this is that for each notif, we can instead get it from our cache if it's cached
notifications := []*gtsmodel.Notification{} for i, notif := range notifications {
for _, notifID := range notifIDs { // Check cache for notification
notif, err := n.GetNotification(ctx, notifID.ID) nn, cached := n.getNotificationCache(notif.ID)
errP := processErrorResponse(err) if cached {
if errP != nil { notifications[i] = nn
return nil, errP continue
}
// Check DB for notification
err := n.getNotificationDB(ctx, notif.ID, notif)
if err != nil {
return nil, err
} }
notifications = append(notifications, notif)
} }
return notifications, nil return notifications, nil
} }
func (n *notificationDB) getNotificationCache(id string) (*gtsmodel.Notification, bool) {
v, ok := n.cache.Get(id)
if !ok {
return nil, false
}
return v.(*gtsmodel.Notification), true
}
func (n *notificationDB) putNotificationCache(notif *gtsmodel.Notification) {
n.cache.Set(notif.ID, notif)
}
func (n *notificationDB) getNotificationDB(ctx context.Context, id string, dst *gtsmodel.Notification) error {
q := n.newNotificationQ(dst).
Where("notification.id = ?", id)
err := q.Scan(ctx)
if err != nil {
return n.conn.ProcessError(err)
}
n.putNotificationCache(dst)
return nil
}

View file

@ -23,7 +23,6 @@ import (
"database/sql" "database/sql"
"fmt" "fmt"
"github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
@ -32,8 +31,7 @@ import (
type relationshipDB struct { type relationshipDB struct {
config *config.Config config *config.Config
conn *bun.DB conn *DBConn
log *logrus.Logger
} }
func (r *relationshipDB) newBlockQ(block *gtsmodel.Block) *bun.SelectQuery { func (r *relationshipDB) newBlockQ(block *gtsmodel.Block) *bun.SelectQuery {
@ -66,7 +64,7 @@ func (r *relationshipDB) IsBlocked(ctx context.Context, account1 string, account
Where("account_id = ?", account2) Where("account_id = ?", account2)
} }
return exists(ctx, q) return r.conn.Exists(ctx, q)
} }
func (r *relationshipDB) GetBlock(ctx context.Context, account1 string, account2 string) (*gtsmodel.Block, db.Error) { func (r *relationshipDB) GetBlock(ctx context.Context, account1 string, account2 string) (*gtsmodel.Block, db.Error) {
@ -76,9 +74,11 @@ func (r *relationshipDB) GetBlock(ctx context.Context, account1 string, account2
Where("block.account_id = ?", account1). Where("block.account_id = ?", account1).
Where("block.target_account_id = ?", account2) Where("block.target_account_id = ?", account2)
err := processErrorResponse(q.Scan(ctx)) err := q.Scan(ctx)
if err != nil {
return block, err return nil, r.conn.ProcessError(err)
}
return block, nil
} }
func (r *relationshipDB) GetRelationship(ctx context.Context, requestingAccount string, targetAccount string) (*gtsmodel.Relationship, db.Error) { func (r *relationshipDB) GetRelationship(ctx context.Context, requestingAccount string, targetAccount string) (*gtsmodel.Relationship, db.Error) {
@ -176,7 +176,7 @@ func (r *relationshipDB) IsFollowing(ctx context.Context, sourceAccount *gtsmode
Where("target_account_id = ?", targetAccount.ID). Where("target_account_id = ?", targetAccount.ID).
Limit(1) Limit(1)
return exists(ctx, q) return r.conn.Exists(ctx, q)
} }
func (r *relationshipDB) IsFollowRequested(ctx context.Context, sourceAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) (bool, db.Error) { func (r *relationshipDB) IsFollowRequested(ctx context.Context, sourceAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) (bool, db.Error) {
@ -190,7 +190,7 @@ func (r *relationshipDB) IsFollowRequested(ctx context.Context, sourceAccount *g
Where("account_id = ?", sourceAccount.ID). Where("account_id = ?", sourceAccount.ID).
Where("target_account_id = ?", targetAccount.ID) Where("target_account_id = ?", targetAccount.ID)
return exists(ctx, q) return r.conn.Exists(ctx, q)
} }
func (r *relationshipDB) IsMutualFollowing(ctx context.Context, account1 *gtsmodel.Account, account2 *gtsmodel.Account) (bool, db.Error) { func (r *relationshipDB) IsMutualFollowing(ctx context.Context, account1 *gtsmodel.Account, account2 *gtsmodel.Account) (bool, db.Error) {
@ -201,13 +201,13 @@ func (r *relationshipDB) IsMutualFollowing(ctx context.Context, account1 *gtsmod
// make sure account 1 follows account 2 // make sure account 1 follows account 2
f1, err := r.IsFollowing(ctx, account1, account2) f1, err := r.IsFollowing(ctx, account1, account2)
if err != nil { if err != nil {
return false, processErrorResponse(err) return false, err
} }
// make sure account 2 follows account 1 // make sure account 2 follows account 1
f2, err := r.IsFollowing(ctx, account2, account1) f2, err := r.IsFollowing(ctx, account2, account1)
if err != nil { if err != nil {
return false, processErrorResponse(err) return false, err
} }
return f1 && f2, nil return f1 && f2, nil
@ -222,7 +222,7 @@ func (r *relationshipDB) AcceptFollowRequest(ctx context.Context, originAccountI
Where("account_id = ?", originAccountID). Where("account_id = ?", originAccountID).
Where("target_account_id = ?", targetAccountID). Where("target_account_id = ?", targetAccountID).
Scan(ctx); err != nil { Scan(ctx); err != nil {
return nil, processErrorResponse(err) return nil, r.conn.ProcessError(err)
} }
// create a new follow to 'replace' the request with // create a new follow to 'replace' the request with
@ -239,7 +239,7 @@ func (r *relationshipDB) AcceptFollowRequest(ctx context.Context, originAccountI
Model(follow). Model(follow).
On("CONFLICT ON CONSTRAINT follows_account_id_target_account_id_key DO UPDATE set uri = ?", follow.URI). On("CONFLICT ON CONSTRAINT follows_account_id_target_account_id_key DO UPDATE set uri = ?", follow.URI).
Exec(ctx); err != nil { Exec(ctx); err != nil {
return nil, processErrorResponse(err) return nil, r.conn.ProcessError(err)
} }
// now remove the follow request // now remove the follow request
@ -249,7 +249,7 @@ func (r *relationshipDB) AcceptFollowRequest(ctx context.Context, originAccountI
Where("account_id = ?", originAccountID). Where("account_id = ?", originAccountID).
Where("target_account_id = ?", targetAccountID). Where("target_account_id = ?", targetAccountID).
Exec(ctx); err != nil { Exec(ctx); err != nil {
return nil, processErrorResponse(err) return nil, r.conn.ProcessError(err)
} }
return follow, nil return follow, nil
@ -261,9 +261,11 @@ func (r *relationshipDB) GetAccountFollowRequests(ctx context.Context, accountID
q := r.newFollowQ(&followRequests). q := r.newFollowQ(&followRequests).
Where("target_account_id = ?", accountID) Where("target_account_id = ?", accountID)
err := processErrorResponse(q.Scan(ctx)) err := q.Scan(ctx)
if err != nil {
return followRequests, err return nil, r.conn.ProcessError(err)
}
return followRequests, nil
} }
func (r *relationshipDB) GetAccountFollows(ctx context.Context, accountID string) ([]*gtsmodel.Follow, db.Error) { func (r *relationshipDB) GetAccountFollows(ctx context.Context, accountID string) ([]*gtsmodel.Follow, db.Error) {
@ -272,9 +274,11 @@ func (r *relationshipDB) GetAccountFollows(ctx context.Context, accountID string
q := r.newFollowQ(&follows). q := r.newFollowQ(&follows).
Where("account_id = ?", accountID) Where("account_id = ?", accountID)
err := processErrorResponse(q.Scan(ctx)) err := q.Scan(ctx)
if err != nil {
return follows, err return nil, r.conn.ProcessError(err)
}
return follows, nil
} }
func (r *relationshipDB) CountAccountFollows(ctx context.Context, accountID string, localOnly bool) (int, db.Error) { func (r *relationshipDB) CountAccountFollows(ctx context.Context, accountID string, localOnly bool) (int, db.Error) {
@ -286,7 +290,6 @@ func (r *relationshipDB) CountAccountFollows(ctx context.Context, accountID stri
} }
func (r *relationshipDB) GetAccountFollowedBy(ctx context.Context, accountID string, localOnly bool) ([]*gtsmodel.Follow, db.Error) { func (r *relationshipDB) GetAccountFollowedBy(ctx context.Context, accountID string, localOnly bool) ([]*gtsmodel.Follow, db.Error) {
follows := []*gtsmodel.Follow{} follows := []*gtsmodel.Follow{}
q := r.conn. q := r.conn.
@ -302,11 +305,9 @@ func (r *relationshipDB) GetAccountFollowedBy(ctx context.Context, accountID str
q = q.Where("target_account_id = ?", accountID) q = q.Where("target_account_id = ?", accountID)
} }
if err := q.Scan(ctx); err != nil { err := q.Scan(ctx)
if err == sql.ErrNoRows { if err != nil && err != sql.ErrNoRows {
return follows, nil return nil, r.conn.ProcessError(err)
}
return nil, processErrorResponse(err)
} }
return follows, nil return follows, nil
} }

View file

@ -23,22 +23,19 @@ import (
"crypto/rand" "crypto/rand"
"errors" "errors"
"github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/id" "github.com/superseriousbusiness/gotosocial/internal/id"
"github.com/uptrace/bun"
) )
type sessionDB struct { type sessionDB struct {
config *config.Config config *config.Config
conn *bun.DB conn *DBConn
log *logrus.Logger
} }
func (s *sessionDB) GetSession(ctx context.Context) (*gtsmodel.RouterSession, db.Error) { func (s *sessionDB) GetSession(ctx context.Context) (*gtsmodel.RouterSession, db.Error) {
rss := []*gtsmodel.RouterSession{} rss := make([]*gtsmodel.RouterSession, 0, 1)
_, err := s.conn. _, err := s.conn.
NewSelect(). NewSelect().
@ -47,7 +44,7 @@ func (s *sessionDB) GetSession(ctx context.Context) (*gtsmodel.RouterSession, db
Order("id DESC"). Order("id DESC").
Exec(ctx) Exec(ctx)
if err != nil { if err != nil {
return nil, processErrorResponse(err) return nil, s.conn.ProcessError(err)
} }
if len(rss) <= 0 { if len(rss) <= 0 {
@ -92,8 +89,8 @@ func (s *sessionDB) createSession(ctx context.Context) (*gtsmodel.RouterSession,
Model(rs) Model(rs)
_, err = q.Exec(ctx) _, err = q.Exec(ctx)
if err != nil {
err = processErrorResponse(err) return nil, s.conn.ProcessError(err)
}
return rs, err return rs, nil
} }

Binary file not shown.

View file

@ -24,7 +24,6 @@ import (
"errors" "errors"
"time" "time"
"github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/cache" "github.com/superseriousbusiness/gotosocial/internal/cache"
"github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/db"
@ -34,38 +33,8 @@ import (
type statusDB struct { type statusDB struct {
config *config.Config config *config.Config
conn *bun.DB conn *DBConn
log *logrus.Logger cache *cache.StatusCache
cache cache.Cache
}
func (s *statusDB) cacheStatus(id string, status *gtsmodel.Status) {
if s.cache == nil {
s.cache = cache.New()
}
if err := s.cache.Store(id, status); err != nil {
s.log.Panicf("statusDB: error storing in cache: %s", err)
}
}
func (s *statusDB) statusCached(id string) (*gtsmodel.Status, bool) {
if s.cache == nil {
s.cache = cache.New()
return nil, false
}
sI, err := s.cache.Fetch(id)
if err != nil || sI == nil {
return nil, false
}
status, ok := sI.(*gtsmodel.Status)
if !ok {
s.log.Panicf("statusDB: cached interface with key %s was not a status", id)
}
return status, true
} }
func (s *statusDB) newStatusQ(status interface{}) *bun.SelectQuery { func (s *statusDB) newStatusQ(status interface{}) *bun.SelectQuery {
@ -84,7 +53,9 @@ func (s *statusDB) newStatusQ(status interface{}) *bun.SelectQuery {
func (s *statusDB) getAttachedStatuses(ctx context.Context, status *gtsmodel.Status) *gtsmodel.Status { func (s *statusDB) getAttachedStatuses(ctx context.Context, status *gtsmodel.Status) *gtsmodel.Status {
if status.InReplyToID != "" && status.InReplyTo == nil { if status.InReplyToID != "" && status.InReplyTo == nil {
if inReplyTo, cached := s.statusCached(status.InReplyToID); cached { // TODO: do we want to keep this possibly recursive strategy?
if inReplyTo, cached := s.cache.GetByID(status.InReplyToID); cached {
status.InReplyTo = inReplyTo status.InReplyTo = inReplyTo
} else if inReplyTo, err := s.GetStatusByID(ctx, status.InReplyToID); err == nil { } else if inReplyTo, err := s.GetStatusByID(ctx, status.InReplyToID); err == nil {
status.InReplyTo = inReplyTo status.InReplyTo = inReplyTo
@ -92,7 +63,9 @@ func (s *statusDB) getAttachedStatuses(ctx context.Context, status *gtsmodel.Sta
} }
if status.BoostOfID != "" && status.BoostOf == nil { if status.BoostOfID != "" && status.BoostOf == nil {
if boostOf, cached := s.statusCached(status.BoostOfID); cached { // TODO: do we want to keep this possibly recursive strategy?
if boostOf, cached := s.cache.GetByID(status.BoostOfID); cached {
status.BoostOf = boostOf status.BoostOf = boostOf
} else if boostOf, err := s.GetStatusByID(ctx, status.BoostOfID); err == nil { } else if boostOf, err := s.GetStatusByID(ctx, status.BoostOfID); err == nil {
status.BoostOf = boostOf status.BoostOf = boostOf
@ -112,29 +85,26 @@ func (s *statusDB) newFaveQ(faves interface{}) *bun.SelectQuery {
} }
func (s *statusDB) GetStatusByID(ctx context.Context, id string) (*gtsmodel.Status, db.Error) { func (s *statusDB) GetStatusByID(ctx context.Context, id string) (*gtsmodel.Status, db.Error) {
if status, cached := s.statusCached(id); cached { if status, cached := s.cache.GetByID(id); cached {
return status, nil return status, nil
} }
status := new(gtsmodel.Status) status := &gtsmodel.Status{}
q := s.newStatusQ(status). q := s.newStatusQ(status).
Where("status.id = ?", id) Where("status.id = ?", id)
err := processErrorResponse(q.Scan(ctx)) err := q.Scan(ctx)
if err != nil { if err != nil {
return nil, err return nil, s.conn.ProcessError(err)
} }
if status != nil { s.cache.Put(status)
s.cacheStatus(id, status) return s.getAttachedStatuses(ctx, status), nil
}
return s.getAttachedStatuses(ctx, status), err
} }
func (s *statusDB) GetStatusByURI(ctx context.Context, uri string) (*gtsmodel.Status, db.Error) { func (s *statusDB) GetStatusByURI(ctx context.Context, uri string) (*gtsmodel.Status, db.Error) {
if status, cached := s.statusCached(uri); cached { if status, cached := s.cache.GetByURI(uri); cached {
return status, nil return status, nil
} }
@ -143,38 +113,32 @@ func (s *statusDB) GetStatusByURI(ctx context.Context, uri string) (*gtsmodel.St
q := s.newStatusQ(status). q := s.newStatusQ(status).
Where("LOWER(status.uri) = LOWER(?)", uri) Where("LOWER(status.uri) = LOWER(?)", uri)
err := processErrorResponse(q.Scan(ctx)) err := q.Scan(ctx)
if err != nil { if err != nil {
return nil, err return nil, s.conn.ProcessError(err)
} }
if status != nil { s.cache.Put(status)
s.cacheStatus(uri, status) return s.getAttachedStatuses(ctx, status), nil
} }
return s.getAttachedStatuses(ctx, status), err func (s *statusDB) GetStatusByURL(ctx context.Context, url string) (*gtsmodel.Status, db.Error) {
} if status, cached := s.cache.GetByURL(url); cached {
func (s *statusDB) GetStatusByURL(ctx context.Context, uri string) (*gtsmodel.Status, db.Error) {
if status, cached := s.statusCached(uri); cached {
return status, nil return status, nil
} }
status := &gtsmodel.Status{} status := &gtsmodel.Status{}
q := s.newStatusQ(status). q := s.newStatusQ(status).
Where("LOWER(status.url) = LOWER(?)", uri) Where("LOWER(status.url) = LOWER(?)", url)
err := processErrorResponse(q.Scan(ctx)) err := q.Scan(ctx)
if err != nil { if err != nil {
return nil, err return nil, s.conn.ProcessError(err)
} }
if status != nil { s.cache.Put(status)
s.cacheStatus(uri, status) return s.getAttachedStatuses(ctx, status), nil
}
return s.getAttachedStatuses(ctx, status), err
} }
func (s *statusDB) PutStatus(ctx context.Context, status *gtsmodel.Status) db.Error { func (s *statusDB) PutStatus(ctx context.Context, status *gtsmodel.Status) db.Error {
@ -213,14 +177,12 @@ func (s *statusDB) PutStatus(ctx context.Context, status *gtsmodel.Status) db.Er
_, err := tx.NewInsert().Model(status).Exec(ctx) _, err := tx.NewInsert().Model(status).Exec(ctx)
return err return err
} }
return s.conn.ProcessError(s.conn.RunInTx(ctx, nil, transaction))
return processErrorResponse(s.conn.RunInTx(ctx, nil, transaction))
} }
func (s *statusDB) GetStatusParents(ctx context.Context, status *gtsmodel.Status, onlyDirect bool) ([]*gtsmodel.Status, db.Error) { func (s *statusDB) GetStatusParents(ctx context.Context, status *gtsmodel.Status, onlyDirect bool) ([]*gtsmodel.Status, db.Error) {
parents := []*gtsmodel.Status{} parents := []*gtsmodel.Status{}
s.statusParent(ctx, status, &parents, onlyDirect) s.statusParent(ctx, status, &parents, onlyDirect)
return parents, nil return parents, nil
} }
@ -318,7 +280,7 @@ func (s *statusDB) IsStatusFavedBy(ctx context.Context, status *gtsmodel.Status,
Where("status_id = ?", status.ID). Where("status_id = ?", status.ID).
Where("account_id = ?", accountID) Where("account_id = ?", accountID)
return exists(ctx, q) return s.conn.Exists(ctx, q)
} }
func (s *statusDB) IsStatusRebloggedBy(ctx context.Context, status *gtsmodel.Status, accountID string) (bool, db.Error) { func (s *statusDB) IsStatusRebloggedBy(ctx context.Context, status *gtsmodel.Status, accountID string) (bool, db.Error) {
@ -328,7 +290,7 @@ func (s *statusDB) IsStatusRebloggedBy(ctx context.Context, status *gtsmodel.Sta
Where("boost_of_id = ?", status.ID). Where("boost_of_id = ?", status.ID).
Where("account_id = ?", accountID) Where("account_id = ?", accountID)
return exists(ctx, q) return s.conn.Exists(ctx, q)
} }
func (s *statusDB) IsStatusMutedBy(ctx context.Context, status *gtsmodel.Status, accountID string) (bool, db.Error) { func (s *statusDB) IsStatusMutedBy(ctx context.Context, status *gtsmodel.Status, accountID string) (bool, db.Error) {
@ -338,7 +300,7 @@ func (s *statusDB) IsStatusMutedBy(ctx context.Context, status *gtsmodel.Status,
Where("status_id = ?", status.ID). Where("status_id = ?", status.ID).
Where("account_id = ?", accountID) Where("account_id = ?", accountID)
return exists(ctx, q) return s.conn.Exists(ctx, q)
} }
func (s *statusDB) IsStatusBookmarkedBy(ctx context.Context, status *gtsmodel.Status, accountID string) (bool, db.Error) { func (s *statusDB) IsStatusBookmarkedBy(ctx context.Context, status *gtsmodel.Status, accountID string) (bool, db.Error) {
@ -348,7 +310,7 @@ func (s *statusDB) IsStatusBookmarkedBy(ctx context.Context, status *gtsmodel.St
Where("status_id = ?", status.ID). Where("status_id = ?", status.ID).
Where("account_id = ?", accountID) Where("account_id = ?", accountID)
return exists(ctx, q) return s.conn.Exists(ctx, q)
} }
func (s *statusDB) GetStatusFaves(ctx context.Context, status *gtsmodel.Status) ([]*gtsmodel.StatusFave, db.Error) { func (s *statusDB) GetStatusFaves(ctx context.Context, status *gtsmodel.Status) ([]*gtsmodel.StatusFave, db.Error) {
@ -357,8 +319,11 @@ func (s *statusDB) GetStatusFaves(ctx context.Context, status *gtsmodel.Status)
q := s.newFaveQ(&faves). q := s.newFaveQ(&faves).
Where("status_id = ?", status.ID) Where("status_id = ?", status.ID)
err := processErrorResponse(q.Scan(ctx)) err := q.Scan(ctx)
return faves, err if err != nil {
return nil, s.conn.ProcessError(err)
}
return faves, nil
} }
func (s *statusDB) GetStatusReblogs(ctx context.Context, status *gtsmodel.Status) ([]*gtsmodel.Status, db.Error) { func (s *statusDB) GetStatusReblogs(ctx context.Context, status *gtsmodel.Status) ([]*gtsmodel.Status, db.Error) {
@ -367,6 +332,9 @@ func (s *statusDB) GetStatusReblogs(ctx context.Context, status *gtsmodel.Status
q := s.newStatusQ(&reblogs). q := s.newStatusQ(&reblogs).
Where("boost_of_id = ?", status.ID) Where("boost_of_id = ?", status.ID)
err := processErrorResponse(q.Scan(ctx)) err := q.Scan(ctx)
return reblogs, err if err != nil {
return nil, s.conn.ProcessError(err)
}
return reblogs, nil
} }

View file

@ -59,7 +59,6 @@ func (suite *StatusTestSuite) TearDownTest() {
func (suite *StatusTestSuite) TestGetStatusByID() { func (suite *StatusTestSuite) TestGetStatusByID() {
status, err := suite.db.GetStatusByID(context.Background(), suite.testStatuses["local_account_1_status_1"].ID) status, err := suite.db.GetStatusByID(context.Background(), suite.testStatuses["local_account_1_status_1"].ID)
if err != nil { if err != nil {
fmt.Println(err.Error())
suite.FailNow(err.Error()) suite.FailNow(err.Error())
} }
suite.NotNil(status) suite.NotNil(status)

View file

@ -23,7 +23,6 @@ import (
"database/sql" "database/sql"
"sort" "sort"
"github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
@ -32,12 +31,18 @@ import (
type timelineDB struct { type timelineDB struct {
config *config.Config config *config.Config
conn *bun.DB conn *DBConn
log *logrus.Logger
} }
func (t *timelineDB) GetHomeTimeline(ctx context.Context, accountID string, maxID string, sinceID string, minID string, limit int, local bool) ([]*gtsmodel.Status, db.Error) { func (t *timelineDB) GetHomeTimeline(ctx context.Context, accountID string, maxID string, sinceID string, minID string, limit int, local bool) ([]*gtsmodel.Status, db.Error) {
statuses := []*gtsmodel.Status{} // Ensure reasonable
if limit < 0 {
limit = 0
}
// Make educated guess for slice size
statuses := make([]*gtsmodel.Status, 0, limit)
q := t.conn. q := t.conn.
NewSelect(). NewSelect().
Model(&statuses) Model(&statuses)
@ -86,11 +91,21 @@ func (t *timelineDB) GetHomeTimeline(ctx context.Context, accountID string, maxI
q = q.WhereGroup(" AND ", whereGroup) q = q.WhereGroup(" AND ", whereGroup)
return statuses, processErrorResponse(q.Scan(ctx)) err := q.Scan(ctx)
if err != nil {
return nil, t.conn.ProcessError(err)
}
return statuses, nil
} }
func (t *timelineDB) GetPublicTimeline(ctx context.Context, accountID string, maxID string, sinceID string, minID string, limit int, local bool) ([]*gtsmodel.Status, db.Error) { func (t *timelineDB) GetPublicTimeline(ctx context.Context, accountID string, maxID string, sinceID string, minID string, limit int, local bool) ([]*gtsmodel.Status, db.Error) {
statuses := []*gtsmodel.Status{} // Ensure reasonable
if limit < 0 {
limit = 0
}
// Make educated guess for slice size
statuses := make([]*gtsmodel.Status, 0, limit)
q := t.conn. q := t.conn.
NewSelect(). NewSelect().
@ -121,14 +136,23 @@ func (t *timelineDB) GetPublicTimeline(ctx context.Context, accountID string, ma
q = q.Limit(limit) q = q.Limit(limit)
} }
return statuses, processErrorResponse(q.Scan(ctx)) err := q.Scan(ctx)
if err != nil {
return nil, t.conn.ProcessError(err)
}
return statuses, nil
} }
// TODO optimize this query and the logic here, because it's slow as balls -- it takes like a literal second to return with a limit of 20! // TODO optimize this query and the logic here, because it's slow as balls -- it takes like a literal second to return with a limit of 20!
// It might be worth serving it through a timeline instead of raw DB queries, like we do for Home feeds. // It might be worth serving it through a timeline instead of raw DB queries, like we do for Home feeds.
func (t *timelineDB) GetFavedTimeline(ctx context.Context, accountID string, maxID string, minID string, limit int) ([]*gtsmodel.Status, string, string, db.Error) { func (t *timelineDB) GetFavedTimeline(ctx context.Context, accountID string, maxID string, minID string, limit int) ([]*gtsmodel.Status, string, string, db.Error) {
// Ensure reasonable
if limit < 0 {
limit = 0
}
faves := []*gtsmodel.StatusFave{} // Make educated guess for slice size
faves := make([]*gtsmodel.StatusFave, 0, limit)
fq := t.conn. fq := t.conn.
NewSelect(). NewSelect().
@ -160,26 +184,23 @@ func (t *timelineDB) GetFavedTimeline(ctx context.Context, accountID string, max
return nil, "", "", db.ErrNoEntries return nil, "", "", db.ErrNoEntries
} }
// map[statusID]faveID -- we need this to sort statuses by fave ID rather than their own ID // map[statusID]faveID -- we need this to sort statuses by fave ID rather than status ID
statusesFavesMap := map[string]string{} statusesFavesMap := make(map[string]string, len(faves))
statusIDs := make([]string, 0, len(faves))
in := []string{}
for _, f := range faves { for _, f := range faves {
statusesFavesMap[f.StatusID] = f.ID statusesFavesMap[f.StatusID] = f.ID
in = append(in, f.StatusID) statusIDs = append(statusIDs, f.StatusID)
} }
statuses := []*gtsmodel.Status{} statuses := make([]*gtsmodel.Status, 0, len(statusIDs))
err = t.conn. err = t.conn.
NewSelect(). NewSelect().
Model(&statuses). Model(&statuses).
Where("id IN (?)", bun.In(in)). Where("id IN (?)", bun.In(statusIDs)).
Scan(ctx) Scan(ctx)
if err != nil { if err != nil {
if err == sql.ErrNoRows { return nil, "", "", t.conn.ProcessError(err)
return nil, "", "", db.ErrNoEntries
}
return nil, "", "", err
} }
if len(statuses) == 0 { if len(statuses) == 0 {

View file

@ -19,64 +19,9 @@
package bundb package bundb
import ( import (
"context"
"strings"
"database/sql"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/uptrace/bun" "github.com/uptrace/bun"
) )
// processErrorResponse parses the given error and returns an appropriate DBError.
func processErrorResponse(err error) db.Error {
switch err {
case nil:
return nil
case sql.ErrNoRows:
return db.ErrNoEntries
default:
if strings.Contains(err.Error(), "duplicate key value violates unique constraint") {
return db.ErrAlreadyExists
}
return err
}
}
func exists(ctx context.Context, q *bun.SelectQuery) (bool, db.Error) {
count, err := q.Count(ctx)
exists := count != 0
err = processErrorResponse(err)
if err != nil {
if err == db.ErrNoEntries {
return false, nil
}
return false, err
}
return exists, nil
}
func notExists(ctx context.Context, q *bun.SelectQuery) (bool, db.Error) {
count, err := q.Count(ctx)
notExists := count == 0
err = processErrorResponse(err)
if err != nil {
if err == db.ErrNoEntries {
return true, nil
}
return false, err
}
return notExists, nil
}
// whereEmptyOrNull is a convenience function to return a bun WhereGroup that specifies // whereEmptyOrNull is a convenience function to return a bun WhereGroup that specifies
// that the given column should be EITHER an empty string OR null. // that the given column should be EITHER an empty string OR null.
// //

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

19
vendor/github.com/kballard/go-shellquote/LICENSE generated vendored Normal file
View file

@ -0,0 +1,19 @@
Copyright (C) 2014 Kevin Ballard
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

36
vendor/github.com/kballard/go-shellquote/README generated vendored Normal file
View file

@ -0,0 +1,36 @@
PACKAGE
package shellquote
import "github.com/kballard/go-shellquote"
Shellquote provides utilities for joining/splitting strings using sh's
word-splitting rules.
VARIABLES
var (
UnterminatedSingleQuoteError = errors.New("Unterminated single-quoted string")
UnterminatedDoubleQuoteError = errors.New("Unterminated double-quoted string")
UnterminatedEscapeError = errors.New("Unterminated backslash-escape")
)
FUNCTIONS
func Join(args ...string) string
Join quotes each argument and joins them with a space. If passed to
/bin/sh, the resulting string will be split back into the original
arguments.
func Split(input string) (words []string, err error)
Split splits a string according to /bin/sh's word-splitting rules. It
supports backslash-escapes, single-quotes, and double-quotes. Notably it
does not support the $'' style of quoting. It also doesn't attempt to
perform any other sort of expansion, including brace expansion, shell
expansion, or pathname expansion.
If the given input has an unterminated quoted string or ends in a
backslash-escape, one of UnterminatedSingleQuoteError,
UnterminatedDoubleQuoteError, or UnterminatedEscapeError is returned.

3
vendor/github.com/kballard/go-shellquote/doc.go generated vendored Normal file
View file

@ -0,0 +1,3 @@
// Shellquote provides utilities for joining/splitting strings using sh's
// word-splitting rules.
package shellquote

102
vendor/github.com/kballard/go-shellquote/quote.go generated vendored Normal file
View file

@ -0,0 +1,102 @@
package shellquote
import (
"bytes"
"strings"
"unicode/utf8"
)
// Join quotes each argument and joins them with a space.
// If passed to /bin/sh, the resulting string will be split back into the
// original arguments.
func Join(args ...string) string {
var buf bytes.Buffer
for i, arg := range args {
if i != 0 {
buf.WriteByte(' ')
}
quote(arg, &buf)
}
return buf.String()
}
const (
specialChars = "\\'\"`${[|&;<>()*?!"
extraSpecialChars = " \t\n"
prefixChars = "~"
)
func quote(word string, buf *bytes.Buffer) {
// We want to try to produce a "nice" output. As such, we will
// backslash-escape most characters, but if we encounter a space, or if we
// encounter an extra-special char (which doesn't work with
// backslash-escaping) we switch over to quoting the whole word. We do this
// with a space because it's typically easier for people to read multi-word
// arguments when quoted with a space rather than with ugly backslashes
// everywhere.
origLen := buf.Len()
if len(word) == 0 {
// oops, no content
buf.WriteString("''")
return
}
cur, prev := word, word
atStart := true
for len(cur) > 0 {
c, l := utf8.DecodeRuneInString(cur)
cur = cur[l:]
if strings.ContainsRune(specialChars, c) || (atStart && strings.ContainsRune(prefixChars, c)) {
// copy the non-special chars up to this point
if len(cur) < len(prev) {
buf.WriteString(prev[0 : len(prev)-len(cur)-l])
}
buf.WriteByte('\\')
buf.WriteRune(c)
prev = cur
} else if strings.ContainsRune(extraSpecialChars, c) {
// start over in quote mode
buf.Truncate(origLen)
goto quote
}
atStart = false
}
if len(prev) > 0 {
buf.WriteString(prev)
}
return
quote:
// quote mode
// Use single-quotes, but if we find a single-quote in the word, we need
// to terminate the string, emit an escaped quote, and start the string up
// again
inQuote := false
for len(word) > 0 {
i := strings.IndexRune(word, '\'')
if i == -1 {
break
}
if i > 0 {
if !inQuote {
buf.WriteByte('\'')
inQuote = true
}
buf.WriteString(word[0:i])
}
word = word[i+1:]
if inQuote {
buf.WriteByte('\'')
inQuote = false
}
buf.WriteString("\\'")
}
if len(word) > 0 {
if !inQuote {
buf.WriteByte('\'')
}
buf.WriteString(word)
buf.WriteByte('\'')
}
}

156
vendor/github.com/kballard/go-shellquote/unquote.go generated vendored Normal file
View file

@ -0,0 +1,156 @@
package shellquote
import (
"bytes"
"errors"
"strings"
"unicode/utf8"
)
var (
UnterminatedSingleQuoteError = errors.New("Unterminated single-quoted string")
UnterminatedDoubleQuoteError = errors.New("Unterminated double-quoted string")
UnterminatedEscapeError = errors.New("Unterminated backslash-escape")
)
var (
splitChars = " \n\t"
singleChar = '\''
doubleChar = '"'
escapeChar = '\\'
doubleEscapeChars = "$`\"\n\\"
)
// Split splits a string according to /bin/sh's word-splitting rules. It
// supports backslash-escapes, single-quotes, and double-quotes. Notably it does
// not support the $'' style of quoting. It also doesn't attempt to perform any
// other sort of expansion, including brace expansion, shell expansion, or
// pathname expansion.
//
// If the given input has an unterminated quoted string or ends in a
// backslash-escape, one of UnterminatedSingleQuoteError,
// UnterminatedDoubleQuoteError, or UnterminatedEscapeError is returned.
func Split(input string) (words []string, err error) {
var buf bytes.Buffer
words = make([]string, 0)
for len(input) > 0 {
// skip any splitChars at the start
c, l := utf8.DecodeRuneInString(input)
if strings.ContainsRune(splitChars, c) {
input = input[l:]
continue
} else if c == escapeChar {
// Look ahead for escaped newline so we can skip over it
next := input[l:]
if len(next) == 0 {
err = UnterminatedEscapeError
return
}
c2, l2 := utf8.DecodeRuneInString(next)
if c2 == '\n' {
input = next[l2:]
continue
}
}
var word string
word, input, err = splitWord(input, &buf)
if err != nil {
return
}
words = append(words, word)
}
return
}
func splitWord(input string, buf *bytes.Buffer) (word string, remainder string, err error) {
buf.Reset()
raw:
{
cur := input
for len(cur) > 0 {
c, l := utf8.DecodeRuneInString(cur)
cur = cur[l:]
if c == singleChar {
buf.WriteString(input[0 : len(input)-len(cur)-l])
input = cur
goto single
} else if c == doubleChar {
buf.WriteString(input[0 : len(input)-len(cur)-l])
input = cur
goto double
} else if c == escapeChar {
buf.WriteString(input[0 : len(input)-len(cur)-l])
input = cur
goto escape
} else if strings.ContainsRune(splitChars, c) {
buf.WriteString(input[0 : len(input)-len(cur)-l])
return buf.String(), cur, nil
}
}
if len(input) > 0 {
buf.WriteString(input)
input = ""
}
goto done
}
escape:
{
if len(input) == 0 {
return "", "", UnterminatedEscapeError
}
c, l := utf8.DecodeRuneInString(input)
if c == '\n' {
// a backslash-escaped newline is elided from the output entirely
} else {
buf.WriteString(input[:l])
}
input = input[l:]
}
goto raw
single:
{
i := strings.IndexRune(input, singleChar)
if i == -1 {
return "", "", UnterminatedSingleQuoteError
}
buf.WriteString(input[0:i])
input = input[i+1:]
goto raw
}
double:
{
cur := input
for len(cur) > 0 {
c, l := utf8.DecodeRuneInString(cur)
cur = cur[l:]
if c == doubleChar {
buf.WriteString(input[0 : len(input)-len(cur)-l])
input = cur
goto raw
} else if c == escapeChar {
// bash only supports certain escapes in double-quoted strings
c2, l2 := utf8.DecodeRuneInString(cur)
cur = cur[l2:]
if strings.ContainsRune(doubleEscapeChars, c2) {
buf.WriteString(input[0 : len(input)-len(cur)-l-l2])
if c2 == '\n' {
// newline is special, skip the backslash entirely
} else {
buf.WriteRune(c2)
}
input = cur
}
}
}
return "", "", UnterminatedDoubleQuoteError
}
done:
return buf.String(), input, nil
}

27
vendor/github.com/remyoudompheng/bigfft/LICENSE generated vendored Normal file
View file

@ -0,0 +1,27 @@
Copyright (c) 2012 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

43
vendor/github.com/remyoudompheng/bigfft/README generated vendored Normal file
View file

@ -0,0 +1,43 @@
Benchmarking math/big vs. bigfft
Number size old ns/op new ns/op delta
1kb 1599 1640 +2.56%
10kb 61533 62170 +1.04%
50kb 833693 831051 -0.32%
100kb 2567995 2693864 +4.90%
1Mb 105237800 28446400 -72.97%
5Mb 1272947000 168554600 -86.76%
10Mb 3834354000 405120200 -89.43%
20Mb 11514488000 845081600 -92.66%
50Mb 49199945000 2893950000 -94.12%
100Mb 147599836000 5921594000 -95.99%
Benchmarking GMP vs bigfft
Number size GMP ns/op Go ns/op delta
1kb 536 1500 +179.85%
10kb 26669 50777 +90.40%
50kb 252270 658534 +161.04%
100kb 686813 2127534 +209.77%
1Mb 12100000 22391830 +85.06%
5Mb 111731843 133550600 +19.53%
10Mb 212314000 318595800 +50.06%
20Mb 490196000 671512800 +36.99%
50Mb 1280000000 2451476000 +91.52%
100Mb 2673000000 5228991000 +95.62%
Benchmarks were run on a Core 2 Quad Q8200 (2.33GHz).
FFT is enabled when input numbers are over 200kbits.
Scanning large decimal number from strings.
(math/big [n^2 complexity] vs bigfft [n^1.6 complexity], Core i5-4590)
Digits old ns/op new ns/op delta
1e3 9995 10876 +8.81%
1e4 175356 243806 +39.03%
1e5 9427422 6780545 -28.08%
1e6 1776707489 144867502 -91.85%
2e6 6865499995 346540778 -94.95%
5e6 42641034189 1069878799 -97.49%
10e6 151975273589 2693328580 -98.23%

36
vendor/github.com/remyoudompheng/bigfft/arith_386.s generated vendored Normal file
View file

@ -0,0 +1,36 @@
// Trampolines to math/big assembly implementations.
#include "textflag.h"
// func addVV(z, x, y []Word) (c Word)
TEXT ·addVV(SB),NOSPLIT,$0
JMP mathbig·addVV(SB)
// func subVV(z, x, y []Word) (c Word)
TEXT ·subVV(SB),NOSPLIT,$0
JMP mathbig·subVV(SB)
// func addVW(z, x []Word, y Word) (c Word)
TEXT ·addVW(SB),NOSPLIT,$0
JMP mathbig·addVW(SB)
// func subVW(z, x []Word, y Word) (c Word)
TEXT ·subVW(SB),NOSPLIT,$0
JMP mathbig·subVW(SB)
// func shlVU(z, x []Word, s uint) (c Word)
TEXT ·shlVU(SB),NOSPLIT,$0
JMP mathbig·shlVU(SB)
// func shrVU(z, x []Word, s uint) (c Word)
TEXT ·shrVU(SB),NOSPLIT,$0
JMP mathbig·shrVU(SB)
// func mulAddVWW(z, x []Word, y, r Word) (c Word)
TEXT ·mulAddVWW(SB),NOSPLIT,$0
JMP mathbig·mulAddVWW(SB)
// func addMulVVW(z, x []Word, y Word) (c Word)
TEXT ·addMulVVW(SB),NOSPLIT,$0
JMP mathbig·addMulVVW(SB)

38
vendor/github.com/remyoudompheng/bigfft/arith_amd64.s generated vendored Normal file
View file

@ -0,0 +1,38 @@
// Trampolines to math/big assembly implementations.
#include "textflag.h"
// func addVV(z, x, y []Word) (c Word)
TEXT ·addVV(SB),NOSPLIT,$0
JMP mathbig·addVV(SB)
// func subVV(z, x, y []Word) (c Word)
// (same as addVV except for SBBQ instead of ADCQ and label names)
TEXT ·subVV(SB),NOSPLIT,$0
JMP mathbig·subVV(SB)
// func addVW(z, x []Word, y Word) (c Word)
TEXT ·addVW(SB),NOSPLIT,$0
JMP mathbig·addVW(SB)
// func subVW(z, x []Word, y Word) (c Word)
// (same as addVW except for SUBQ/SBBQ instead of ADDQ/ADCQ and label names)
TEXT ·subVW(SB),NOSPLIT,$0
JMP mathbig·subVW(SB)
// func shlVU(z, x []Word, s uint) (c Word)
TEXT ·shlVU(SB),NOSPLIT,$0
JMP mathbig·shlVU(SB)
// func shrVU(z, x []Word, s uint) (c Word)
TEXT ·shrVU(SB),NOSPLIT,$0
JMP mathbig·shrVU(SB)
// func mulAddVWW(z, x []Word, y, r Word) (c Word)
TEXT ·mulAddVWW(SB),NOSPLIT,$0
JMP mathbig·mulAddVWW(SB)
// func addMulVVW(z, x []Word, y Word) (c Word)
TEXT ·addMulVVW(SB),NOSPLIT,$0
JMP mathbig·addMulVVW(SB)

36
vendor/github.com/remyoudompheng/bigfft/arith_arm.s generated vendored Normal file
View file

@ -0,0 +1,36 @@
// Trampolines to math/big assembly implementations.
#include "textflag.h"
// func addVV(z, x, y []Word) (c Word)
TEXT ·addVV(SB),NOSPLIT,$0
B mathbig·addVV(SB)
// func subVV(z, x, y []Word) (c Word)
TEXT ·subVV(SB),NOSPLIT,$0
B mathbig·subVV(SB)
// func addVW(z, x []Word, y Word) (c Word)
TEXT ·addVW(SB),NOSPLIT,$0
B mathbig·addVW(SB)
// func subVW(z, x []Word, y Word) (c Word)
TEXT ·subVW(SB),NOSPLIT,$0
B mathbig·subVW(SB)
// func shlVU(z, x []Word, s uint) (c Word)
TEXT ·shlVU(SB),NOSPLIT,$0
B mathbig·shlVU(SB)
// func shrVU(z, x []Word, s uint) (c Word)
TEXT ·shrVU(SB),NOSPLIT,$0
B mathbig·shrVU(SB)
// func mulAddVWW(z, x []Word, y, r Word) (c Word)
TEXT ·mulAddVWW(SB),NOSPLIT,$0
B mathbig·mulAddVWW(SB)
// func addMulVVW(z, x []Word, y Word) (c Word)
TEXT ·addMulVVW(SB),NOSPLIT,$0
B mathbig·addMulVVW(SB)

36
vendor/github.com/remyoudompheng/bigfft/arith_arm64.s generated vendored Normal file
View file

@ -0,0 +1,36 @@
// Trampolines to math/big assembly implementations.
#include "textflag.h"
// func addVV(z, x, y []Word) (c Word)
TEXT ·addVV(SB),NOSPLIT,$0
B mathbig·addVV(SB)
// func subVV(z, x, y []Word) (c Word)
TEXT ·subVV(SB),NOSPLIT,$0
B mathbig·subVV(SB)
// func addVW(z, x []Word, y Word) (c Word)
TEXT ·addVW(SB),NOSPLIT,$0
B mathbig·addVW(SB)
// func subVW(z, x []Word, y Word) (c Word)
TEXT ·subVW(SB),NOSPLIT,$0
B mathbig·subVW(SB)
// func shlVU(z, x []Word, s uint) (c Word)
TEXT ·shlVU(SB),NOSPLIT,$0
B mathbig·shlVU(SB)
// func shrVU(z, x []Word, s uint) (c Word)
TEXT ·shrVU(SB),NOSPLIT,$0
B mathbig·shrVU(SB)
// func mulAddVWW(z, x []Word, y, r Word) (c Word)
TEXT ·mulAddVWW(SB),NOSPLIT,$0
B mathbig·mulAddVWW(SB)
// func addMulVVW(z, x []Word, y Word) (c Word)
TEXT ·addMulVVW(SB),NOSPLIT,$0
B mathbig·addMulVVW(SB)

16
vendor/github.com/remyoudompheng/bigfft/arith_decl.go generated vendored Normal file
View file

@ -0,0 +1,16 @@
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package bigfft
import . "math/big"
// implemented in arith_$GOARCH.s
func addVV(z, x, y []Word) (c Word)
func subVV(z, x, y []Word) (c Word)
func addVW(z, x []Word, y Word) (c Word)
func subVW(z, x []Word, y Word) (c Word)
func shlVU(z, x []Word, s uint) (c Word)
func mulAddVWW(z, x []Word, y, r Word) (c Word)
func addMulVVW(z, x []Word, y Word) (c Word)

View file

@ -0,0 +1,40 @@
// Trampolines to math/big assembly implementations.
// +build mips64 mips64le
#include "textflag.h"
// func addVV(z, x, y []Word) (c Word)
TEXT ·addVV(SB),NOSPLIT,$0
JMP mathbig·addVV(SB)
// func subVV(z, x, y []Word) (c Word)
// (same as addVV except for SBBQ instead of ADCQ and label names)
TEXT ·subVV(SB),NOSPLIT,$0
JMP mathbig·subVV(SB)
// func addVW(z, x []Word, y Word) (c Word)
TEXT ·addVW(SB),NOSPLIT,$0
JMP mathbig·addVW(SB)
// func subVW(z, x []Word, y Word) (c Word)
// (same as addVW except for SUBQ/SBBQ instead of ADDQ/ADCQ and label names)
TEXT ·subVW(SB),NOSPLIT,$0
JMP mathbig·subVW(SB)
// func shlVU(z, x []Word, s uint) (c Word)
TEXT ·shlVU(SB),NOSPLIT,$0
JMP mathbig·shlVU(SB)
// func shrVU(z, x []Word, s uint) (c Word)
TEXT ·shrVU(SB),NOSPLIT,$0
JMP mathbig·shrVU(SB)
// func mulAddVWW(z, x []Word, y, r Word) (c Word)
TEXT ·mulAddVWW(SB),NOSPLIT,$0
JMP mathbig·mulAddVWW(SB)
// func addMulVVW(z, x []Word, y Word) (c Word)
TEXT ·addMulVVW(SB),NOSPLIT,$0
JMP mathbig·addMulVVW(SB)

40
vendor/github.com/remyoudompheng/bigfft/arith_mipsx.s generated vendored Normal file
View file

@ -0,0 +1,40 @@
// Trampolines to math/big assembly implementations.
// +build mips mipsle
#include "textflag.h"
// func addVV(z, x, y []Word) (c Word)
TEXT ·addVV(SB),NOSPLIT,$0
JMP mathbig·addVV(SB)
// func subVV(z, x, y []Word) (c Word)
// (same as addVV except for SBBQ instead of ADCQ and label names)
TEXT ·subVV(SB),NOSPLIT,$0
JMP mathbig·subVV(SB)
// func addVW(z, x []Word, y Word) (c Word)
TEXT ·addVW(SB),NOSPLIT,$0
JMP mathbig·addVW(SB)
// func subVW(z, x []Word, y Word) (c Word)
// (same as addVW except for SUBQ/SBBQ instead of ADDQ/ADCQ and label names)
TEXT ·subVW(SB),NOSPLIT,$0
JMP mathbig·subVW(SB)
// func shlVU(z, x []Word, s uint) (c Word)
TEXT ·shlVU(SB),NOSPLIT,$0
JMP mathbig·shlVU(SB)
// func shrVU(z, x []Word, s uint) (c Word)
TEXT ·shrVU(SB),NOSPLIT,$0
JMP mathbig·shrVU(SB)
// func mulAddVWW(z, x []Word, y, r Word) (c Word)
TEXT ·mulAddVWW(SB),NOSPLIT,$0
JMP mathbig·mulAddVWW(SB)
// func addMulVVW(z, x []Word, y Word) (c Word)
TEXT ·addMulVVW(SB),NOSPLIT,$0
JMP mathbig·addMulVVW(SB)

38
vendor/github.com/remyoudompheng/bigfft/arith_ppc64x.s generated vendored Normal file
View file

@ -0,0 +1,38 @@
// Trampolines to math/big assembly implementations.
// +build ppc64 ppc64le
#include "textflag.h"
// func addVV(z, x, y []Word) (c Word)
TEXT ·addVV(SB),NOSPLIT,$0
BR mathbig·addVV(SB)
// func subVV(z, x, y []Word) (c Word)
TEXT ·subVV(SB),NOSPLIT,$0
BR mathbig·subVV(SB)
// func addVW(z, x []Word, y Word) (c Word)
TEXT ·addVW(SB),NOSPLIT,$0
BR mathbig·addVW(SB)
// func subVW(z, x []Word, y Word) (c Word)
TEXT ·subVW(SB),NOSPLIT,$0
BR mathbig·subVW(SB)
// func shlVU(z, x []Word, s uint) (c Word)
TEXT ·shlVU(SB),NOSPLIT,$0
BR mathbig·shlVU(SB)
// func shrVU(z, x []Word, s uint) (c Word)
TEXT ·shrVU(SB),NOSPLIT,$0
BR mathbig·shrVU(SB)
// func mulAddVWW(z, x []Word, y, r Word) (c Word)
TEXT ·mulAddVWW(SB),NOSPLIT,$0
BR mathbig·mulAddVWW(SB)
// func addMulVVW(z, x []Word, y Word) (c Word)
TEXT ·addMulVVW(SB),NOSPLIT,$0
BR mathbig·addMulVVW(SB)

37
vendor/github.com/remyoudompheng/bigfft/arith_s390x.s generated vendored Normal file
View file

@ -0,0 +1,37 @@
// Trampolines to math/big assembly implementations.
#include "textflag.h"
// func addVV(z, x, y []Word) (c Word)
TEXT ·addVV(SB),NOSPLIT,$0
BR mathbig·addVV(SB)
// func subVV(z, x, y []Word) (c Word)
TEXT ·subVV(SB),NOSPLIT,$0
BR mathbig·subVV(SB)
// func addVW(z, x []Word, y Word) (c Word)
TEXT ·addVW(SB),NOSPLIT,$0
BR mathbig·addVW(SB)
// func subVW(z, x []Word, y Word) (c Word)
TEXT ·subVW(SB),NOSPLIT,$0
BR mathbig·subVW(SB)
// func shlVU(z, x []Word, s uint) (c Word)
TEXT ·shlVU(SB),NOSPLIT,$0
BR mathbig·shlVU(SB)
// func shrVU(z, x []Word, s uint) (c Word)
TEXT ·shrVU(SB),NOSPLIT,$0
BR mathbig·shrVU(SB)
// func mulAddVWW(z, x []Word, y, r Word) (c Word)
TEXT ·mulAddVWW(SB),NOSPLIT,$0
BR mathbig·mulAddVWW(SB)
// func addMulVVW(z, x []Word, y Word) (c Word)
TEXT ·addMulVVW(SB),NOSPLIT,$0
BR mathbig·addMulVVW(SB)

216
vendor/github.com/remyoudompheng/bigfft/fermat.go generated vendored Normal file
View file

@ -0,0 +1,216 @@
package bigfft
import (
"math/big"
)
// Arithmetic modulo 2^n+1.
// A fermat of length w+1 represents a number modulo 2^(w*_W) + 1. The last
// word is zero or one. A number has at most two representatives satisfying the
// 0-1 last word constraint.
type fermat nat
func (n fermat) String() string { return nat(n).String() }
func (z fermat) norm() {
n := len(z) - 1
c := z[n]
if c == 0 {
return
}
if z[0] >= c {
z[n] = 0
z[0] -= c
return
}
// z[0] < z[n].
subVW(z, z, c) // Substract c
if c > 1 {
z[n] -= c - 1
c = 1
}
// Add back c.
if z[n] == 1 {
z[n] = 0
return
} else {
addVW(z, z, 1)
}
}
// Shift computes (x << k) mod (2^n+1).
func (z fermat) Shift(x fermat, k int) {
if len(z) != len(x) {
panic("len(z) != len(x) in Shift")
}
n := len(x) - 1
// Shift by n*_W is taking the opposite.
k %= 2 * n * _W
if k < 0 {
k += 2 * n * _W
}
neg := false
if k >= n*_W {
k -= n * _W
neg = true
}
kw, kb := k/_W, k%_W
z[n] = 1 // Add (-1)
if !neg {
for i := 0; i < kw; i++ {
z[i] = 0
}
// Shift left by kw words.
// x = a·2^(n-k) + b
// x<<k = (b<<k) - a
copy(z[kw:], x[:n-kw])
b := subVV(z[:kw+1], z[:kw+1], x[n-kw:])
if z[kw+1] > 0 {
z[kw+1] -= b
} else {
subVW(z[kw+1:], z[kw+1:], b)
}
} else {
for i := kw + 1; i < n; i++ {
z[i] = 0
}
// Shift left and negate, by kw words.
copy(z[:kw+1], x[n-kw:n+1]) // z_low = x_high
b := subVV(z[kw:n], z[kw:n], x[:n-kw]) // z_high -= x_low
z[n] -= b
}
// Add back 1.
if z[n] > 0 {
z[n]--
} else if z[0] < ^big.Word(0) {
z[0]++
} else {
addVW(z, z, 1)
}
// Shift left by kb bits
shlVU(z, z, uint(kb))
z.norm()
}
// ShiftHalf shifts x by k/2 bits the left. Shifting by 1/2 bit
// is multiplication by sqrt(2) mod 2^n+1 which is 2^(3n/4) - 2^(n/4).
// A temporary buffer must be provided in tmp.
func (z fermat) ShiftHalf(x fermat, k int, tmp fermat) {
n := len(z) - 1
if k%2 == 0 {
z.Shift(x, k/2)
return
}
u := (k - 1) / 2
a := u + (3*_W/4)*n
b := u + (_W/4)*n
z.Shift(x, a)
tmp.Shift(x, b)
z.Sub(z, tmp)
}
// Add computes addition mod 2^n+1.
func (z fermat) Add(x, y fermat) fermat {
if len(z) != len(x) {
panic("Add: len(z) != len(x)")
}
addVV(z, x, y) // there cannot be a carry here.
z.norm()
return z
}
// Sub computes substraction mod 2^n+1.
func (z fermat) Sub(x, y fermat) fermat {
if len(z) != len(x) {
panic("Add: len(z) != len(x)")
}
n := len(y) - 1
b := subVV(z[:n], x[:n], y[:n])
b += y[n]
// If b > 0, we need to subtract b<<n, which is the same as adding b.
z[n] = x[n]
if z[0] <= ^big.Word(0)-b {
z[0] += b
} else {
addVW(z, z, b)
}
z.norm()
return z
}
func (z fermat) Mul(x, y fermat) fermat {
if len(x) != len(y) {
panic("Mul: len(x) != len(y)")
}
n := len(x) - 1
if n < 30 {
z = z[:2*n+2]
basicMul(z, x, y)
z = z[:2*n+1]
} else {
var xi, yi, zi big.Int
xi.SetBits(x)
yi.SetBits(y)
zi.SetBits(z)
zb := zi.Mul(&xi, &yi).Bits()
if len(zb) <= n {
// Short product.
copy(z, zb)
for i := len(zb); i < len(z); i++ {
z[i] = 0
}
return z
}
z = zb
}
// len(z) is at most 2n+1.
if len(z) > 2*n+1 {
panic("len(z) > 2n+1")
}
// We now have
// z = z[:n] + 1<<(n*W) * z[n:2n+1]
// which normalizes to:
// z = z[:n] - z[n:2n] + z[2n]
c1 := big.Word(0)
if len(z) > 2*n {
c1 = addVW(z[:n], z[:n], z[2*n])
}
c2 := big.Word(0)
if len(z) >= 2*n {
c2 = subVV(z[:n], z[:n], z[n:2*n])
} else {
m := len(z) - n
c2 = subVV(z[:m], z[:m], z[n:])
c2 = subVW(z[m:n], z[m:n], c2)
}
// Restore carries.
// Substracting z[n] -= c2 is the same
// as z[0] += c2
z = z[:n+1]
z[n] = c1
c := addVW(z, z, c2)
if c != 0 {
panic("impossible")
}
z.norm()
return z
}
// copied from math/big
//
// basicMul multiplies x and y and leaves the result in z.
// The (non-normalized) result is placed in z[0 : len(x) + len(y)].
func basicMul(z, x, y fermat) {
// initialize z
for i := 0; i < len(z); i++ {
z[i] = 0
}
for i, d := range y {
if d != 0 {
z[len(x)+i] = addMulVVW(z[i:i+len(x)], x, d)
}
}
}

370
vendor/github.com/remyoudompheng/bigfft/fft.go generated vendored Normal file
View file

@ -0,0 +1,370 @@
// Package bigfft implements multiplication of big.Int using FFT.
//
// The implementation is based on the Schönhage-Strassen method
// using integer FFT modulo 2^n+1.
package bigfft
import (
"math/big"
"unsafe"
)
const _W = int(unsafe.Sizeof(big.Word(0)) * 8)
type nat []big.Word
func (n nat) String() string {
v := new(big.Int)
v.SetBits(n)
return v.String()
}
// fftThreshold is the size (in words) above which FFT is used over
// Karatsuba from math/big.
//
// TestCalibrate seems to indicate a threshold of 60kbits on 32-bit
// arches and 110kbits on 64-bit arches.
var fftThreshold = 1800
// Mul computes the product x*y and returns z.
// It can be used instead of the Mul method of
// *big.Int from math/big package.
func Mul(x, y *big.Int) *big.Int {
xwords := len(x.Bits())
ywords := len(y.Bits())
if xwords > fftThreshold && ywords > fftThreshold {
return mulFFT(x, y)
}
return new(big.Int).Mul(x, y)
}
func mulFFT(x, y *big.Int) *big.Int {
var xb, yb nat = x.Bits(), y.Bits()
zb := fftmul(xb, yb)
z := new(big.Int)
z.SetBits(zb)
if x.Sign()*y.Sign() < 0 {
z.Neg(z)
}
return z
}
// A FFT size of K=1<<k is adequate when K is about 2*sqrt(N) where
// N = x.Bitlen() + y.Bitlen().
func fftmul(x, y nat) nat {
k, m := fftSize(x, y)
xp := polyFromNat(x, k, m)
yp := polyFromNat(y, k, m)
rp := xp.Mul(&yp)
return rp.Int()
}
// fftSizeThreshold[i] is the maximal size (in bits) where we should use
// fft size i.
var fftSizeThreshold = [...]int64{0, 0, 0,
4 << 10, 8 << 10, 16 << 10, // 5
32 << 10, 64 << 10, 1 << 18, 1 << 20, 3 << 20, // 10
8 << 20, 30 << 20, 100 << 20, 300 << 20, 600 << 20,
}
// returns the FFT length k, m the number of words per chunk
// such that m << k is larger than the number of words
// in x*y.
func fftSize(x, y nat) (k uint, m int) {
words := len(x) + len(y)
bits := int64(words) * int64(_W)
k = uint(len(fftSizeThreshold))
for i := range fftSizeThreshold {
if fftSizeThreshold[i] > bits {
k = uint(i)
break
}
}
// The 1<<k chunks of m words must have N bits so that
// 2^N-1 is larger than x*y. That is, m<<k > words
m = words>>k + 1
return
}
// valueSize returns the length (in words) to use for polynomial
// coefficients, to compute a correct product of polynomials P*Q
// where deg(P*Q) < K (== 1<<k) and where coefficients of P and Q are
// less than b^m (== 1 << (m*_W)).
// The chosen length (in bits) must be a multiple of 1 << (k-extra).
func valueSize(k uint, m int, extra uint) int {
// The coefficients of P*Q are less than b^(2m)*K
// so we need W * valueSize >= 2*m*W+K
n := 2*m*_W + int(k) // necessary bits
K := 1 << (k - extra)
if K < _W {
K = _W
}
n = ((n / K) + 1) * K // round to a multiple of K
return n / _W
}
// poly represents an integer via a polynomial in Z[x]/(x^K+1)
// where K is the FFT length and b^m is the computation basis 1<<(m*_W).
// If P = a[0] + a[1] x + ... a[n] x^(K-1), the associated natural number
// is P(b^m).
type poly struct {
k uint // k is such that K = 1<<k.
m int // the m such that P(b^m) is the original number.
a []nat // a slice of at most K m-word coefficients.
}
// polyFromNat slices the number x into a polynomial
// with 1<<k coefficients made of m words.
func polyFromNat(x nat, k uint, m int) poly {
p := poly{k: k, m: m}
length := len(x)/m + 1
p.a = make([]nat, length)
for i := range p.a {
if len(x) < m {
p.a[i] = make(nat, m)
copy(p.a[i], x)
break
}
p.a[i] = x[:m]
x = x[m:]
}
return p
}
// Int evaluates back a poly to its integer value.
func (p *poly) Int() nat {
length := len(p.a)*p.m + 1
if na := len(p.a); na > 0 {
length += len(p.a[na-1])
}
n := make(nat, length)
m := p.m
np := n
for i := range p.a {
l := len(p.a[i])
c := addVV(np[:l], np[:l], p.a[i])
if np[l] < ^big.Word(0) {
np[l] += c
} else {
addVW(np[l:], np[l:], c)
}
np = np[m:]
}
n = trim(n)
return n
}
func trim(n nat) nat {
for i := range n {
if n[len(n)-1-i] != 0 {
return n[:len(n)-i]
}
}
return nil
}
// Mul multiplies p and q modulo X^K-1, where K = 1<<p.k.
// The product is done via a Fourier transform.
func (p *poly) Mul(q *poly) poly {
// extra=2 because:
// * some power of 2 is a K-th root of unity when n is a multiple of K/2.
// * 2 itself is a square (see fermat.ShiftHalf)
n := valueSize(p.k, p.m, 2)
pv, qv := p.Transform(n), q.Transform(n)
rv := pv.Mul(&qv)
r := rv.InvTransform()
r.m = p.m
return r
}
// A polValues represents the value of a poly at the powers of a
// K-th root of unity θ=2^(l/2) in Z/(b^n+1)Z, where b^n = 2^(K/4*l).
type polValues struct {
k uint // k is such that K = 1<<k.
n int // the length of coefficients, n*_W a multiple of K/4.
values []fermat // a slice of K (n+1)-word values
}
// Transform evaluates p at θ^i for i = 0...K-1, where
// θ is a K-th primitive root of unity in Z/(b^n+1)Z.
func (p *poly) Transform(n int) polValues {
k := p.k
inputbits := make([]big.Word, (n+1)<<k)
input := make([]fermat, 1<<k)
// Now computed q(ω^i) for i = 0 ... K-1
valbits := make([]big.Word, (n+1)<<k)
values := make([]fermat, 1<<k)
for i := range values {
input[i] = inputbits[i*(n+1) : (i+1)*(n+1)]
if i < len(p.a) {
copy(input[i], p.a[i])
}
values[i] = fermat(valbits[i*(n+1) : (i+1)*(n+1)])
}
fourier(values, input, false, n, k)
return polValues{k, n, values}
}
// InvTransform reconstructs p (modulo X^K - 1) from its
// values at θ^i for i = 0..K-1.
func (v *polValues) InvTransform() poly {
k, n := v.k, v.n
// Perform an inverse Fourier transform to recover p.
pbits := make([]big.Word, (n+1)<<k)
p := make([]fermat, 1<<k)
for i := range p {
p[i] = fermat(pbits[i*(n+1) : (i+1)*(n+1)])
}
fourier(p, v.values, true, n, k)
// Divide by K, and untwist q to recover p.
u := make(fermat, n+1)
a := make([]nat, 1<<k)
for i := range p {
u.Shift(p[i], -int(k))
copy(p[i], u)
a[i] = nat(p[i])
}
return poly{k: k, m: 0, a: a}
}
// NTransform evaluates p at θω^i for i = 0...K-1, where
// θ is a (2K)-th primitive root of unity in Z/(b^n+1)Z
// and ω = θ².
func (p *poly) NTransform(n int) polValues {
k := p.k
if len(p.a) >= 1<<k {
panic("Transform: len(p.a) >= 1<<k")
}
// θ is represented as a shift.
θshift := (n * _W) >> k
// p(x) = a_0 + a_1 x + ... + a_{K-1} x^(K-1)
// p(θx) = q(x) where
// q(x) = a_0 + θa_1 x + ... + θ^(K-1) a_{K-1} x^(K-1)
//
// Twist p by θ to obtain q.
tbits := make([]big.Word, (n+1)<<k)
twisted := make([]fermat, 1<<k)
src := make(fermat, n+1)
for i := range twisted {
twisted[i] = fermat(tbits[i*(n+1) : (i+1)*(n+1)])
if i < len(p.a) {
for i := range src {
src[i] = 0
}
copy(src, p.a[i])
twisted[i].Shift(src, θshift*i)
}
}
// Now computed q(ω^i) for i = 0 ... K-1
valbits := make([]big.Word, (n+1)<<k)
values := make([]fermat, 1<<k)
for i := range values {
values[i] = fermat(valbits[i*(n+1) : (i+1)*(n+1)])
}
fourier(values, twisted, false, n, k)
return polValues{k, n, values}
}
// InvTransform reconstructs a polynomial from its values at
// roots of x^K+1. The m field of the returned polynomial
// is unspecified.
func (v *polValues) InvNTransform() poly {
k := v.k
n := v.n
θshift := (n * _W) >> k
// Perform an inverse Fourier transform to recover q.
qbits := make([]big.Word, (n+1)<<k)
q := make([]fermat, 1<<k)
for i := range q {
q[i] = fermat(qbits[i*(n+1) : (i+1)*(n+1)])
}
fourier(q, v.values, true, n, k)
// Divide by K, and untwist q to recover p.
u := make(fermat, n+1)
a := make([]nat, 1<<k)
for i := range q {
u.Shift(q[i], -int(k)-i*θshift)
copy(q[i], u)
a[i] = nat(q[i])
}
return poly{k: k, m: 0, a: a}
}
// fourier performs an unnormalized Fourier transform
// of src, a length 1<<k vector of numbers modulo b^n+1
// where b = 1<<_W.
func fourier(dst []fermat, src []fermat, backward bool, n int, k uint) {
var rec func(dst, src []fermat, size uint)
tmp := make(fermat, n+1) // pre-allocate temporary variables.
tmp2 := make(fermat, n+1) // pre-allocate temporary variables.
// The recursion function of the FFT.
// The root of unity used in the transform is ω=1<<(ω2shift/2).
// The source array may use shifted indices (i.e. the i-th
// element is src[i << idxShift]).
rec = func(dst, src []fermat, size uint) {
idxShift := k - size
ω2shift := (4 * n * _W) >> size
if backward {
ω2shift = -ω2shift
}
// Easy cases.
if len(src[0]) != n+1 || len(dst[0]) != n+1 {
panic("len(src[0]) != n+1 || len(dst[0]) != n+1")
}
switch size {
case 0:
copy(dst[0], src[0])
return
case 1:
dst[0].Add(src[0], src[1<<idxShift]) // dst[0] = src[0] + src[1]
dst[1].Sub(src[0], src[1<<idxShift]) // dst[1] = src[0] - src[1]
return
}
// Let P(x) = src[0] + src[1<<idxShift] * x + ... + src[K-1 << idxShift] * x^(K-1)
// The P(x) = Q1(x²) + x*Q2(x²)
// where Q1's coefficients are src with indices shifted by 1
// where Q2's coefficients are src[1<<idxShift:] with indices shifted by 1
// Split destination vectors in halves.
dst1 := dst[:1<<(size-1)]
dst2 := dst[1<<(size-1):]
// Transform Q1 and Q2 in the halves.
rec(dst1, src, size-1)
rec(dst2, src[1<<idxShift:], size-1)
// Reconstruct P's transform from transforms of Q1 and Q2.
// dst[i] is dst1[i] + ω^i * dst2[i]
// dst[i + 1<<(k-1)] is dst1[i] + ω^(i+K/2) * dst2[i]
//
for i := range dst1 {
tmp.ShiftHalf(dst2[i], i*ω2shift, tmp2) // ω^i * dst2[i]
dst2[i].Sub(dst1[i], tmp)
dst1[i].Add(dst1[i], tmp)
}
}
rec(dst, src, k)
}
// Mul returns the pointwise product of p and q.
func (p *polValues) Mul(q *polValues) (r polValues) {
n := p.n
r.k, r.n = p.k, p.n
r.values = make([]fermat, len(p.values))
bits := make([]big.Word, len(p.values)*(n+1))
buf := make(fermat, 8*n)
for i := range r.values {
r.values[i] = bits[i*(n+1) : (i+1)*(n+1)]
z := buf.Mul(p.values[i], q.values[i])
copy(r.values[i], z)
}
return
}

3
vendor/github.com/remyoudompheng/bigfft/go.mod generated vendored Normal file
View file

@ -0,0 +1,3 @@
module github.com/remyoudompheng/bigfft
go 1.12

70
vendor/github.com/remyoudompheng/bigfft/scan.go generated vendored Normal file
View file

@ -0,0 +1,70 @@
package bigfft
import (
"math/big"
)
// FromDecimalString converts the base 10 string
// representation of a natural (non-negative) number
// into a *big.Int.
// Its asymptotic complexity is less than quadratic.
func FromDecimalString(s string) *big.Int {
var sc scanner
z := new(big.Int)
sc.scan(z, s)
return z
}
type scanner struct {
// powers[i] is 10^(2^i * quadraticScanThreshold).
powers []*big.Int
}
func (s *scanner) chunkSize(size int) (int, *big.Int) {
if size <= quadraticScanThreshold {
panic("size < quadraticScanThreshold")
}
pow := uint(0)
for n := size; n > quadraticScanThreshold; n /= 2 {
pow++
}
// threshold * 2^(pow-1) <= size < threshold * 2^pow
return quadraticScanThreshold << (pow - 1), s.power(pow - 1)
}
func (s *scanner) power(k uint) *big.Int {
for i := len(s.powers); i <= int(k); i++ {
z := new(big.Int)
if i == 0 {
if quadraticScanThreshold%14 != 0 {
panic("quadraticScanThreshold % 14 != 0")
}
z.Exp(big.NewInt(1e14), big.NewInt(quadraticScanThreshold/14), nil)
} else {
z.Mul(s.powers[i-1], s.powers[i-1])
}
s.powers = append(s.powers, z)
}
return s.powers[k]
}
func (s *scanner) scan(z *big.Int, str string) {
if len(str) <= quadraticScanThreshold {
z.SetString(str, 10)
return
}
sz, pow := s.chunkSize(len(str))
// Scan the left half.
s.scan(z, str[:len(str)-sz])
// FIXME: reuse temporaries.
left := Mul(z, pow)
// Scan the right half
s.scan(z, str[len(str)-sz:])
z.Add(z, left)
}
// quadraticScanThreshold is the number of digits
// below which big.Int.SetString is more efficient
// than subquadratic algorithms.
// 1232 digits fit in 4096 bits.
const quadraticScanThreshold = 1232

View file

@ -0,0 +1,24 @@
Copyright (c) 2021 Vladimir Mihailenco. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -0,0 +1,94 @@
package sqlitedialect
import (
"database/sql"
"reflect"
"sync"
"github.com/uptrace/bun/dialect"
"github.com/uptrace/bun/dialect/feature"
"github.com/uptrace/bun/dialect/sqltype"
"github.com/uptrace/bun/schema"
)
type Dialect struct {
tables *schema.Tables
features feature.Feature
appenderMap sync.Map
scannerMap sync.Map
}
func New() *Dialect {
d := new(Dialect)
d.tables = schema.NewTables(d)
d.features = feature.Returning | feature.InsertTableAlias | feature.DeleteTableAlias
return d
}
func (d *Dialect) Init(*sql.DB) {}
func (d *Dialect) Name() dialect.Name {
return dialect.SQLite
}
func (d *Dialect) Features() feature.Feature {
return d.features
}
func (d *Dialect) Tables() *schema.Tables {
return d.tables
}
func (d *Dialect) OnTable(table *schema.Table) {
for _, field := range table.FieldMap {
d.onField(field)
}
}
func (d *Dialect) onField(field *schema.Field) {
// INTEGER PRIMARY KEY is an alias for the ROWID.
// It is safe to convert all ints to INTEGER, because SQLite types don't have size.
switch field.DiscoveredSQLType {
case sqltype.SmallInt, sqltype.BigInt:
field.DiscoveredSQLType = sqltype.Integer
}
}
func (d *Dialect) IdentQuote() byte {
return '"'
}
func (d *Dialect) Append(fmter schema.Formatter, b []byte, v interface{}) []byte {
return schema.Append(fmter, b, v, nil)
}
func (d *Dialect) Appender(typ reflect.Type) schema.AppenderFunc {
if v, ok := d.appenderMap.Load(typ); ok {
return v.(schema.AppenderFunc)
}
fn := schema.Appender(typ, nil)
if v, ok := d.appenderMap.LoadOrStore(typ, fn); ok {
return v.(schema.AppenderFunc)
}
return fn
}
func (d *Dialect) FieldAppender(field *schema.Field) schema.AppenderFunc {
return schema.FieldAppender(d, field)
}
func (d *Dialect) Scanner(typ reflect.Type) schema.ScannerFunc {
if v, ok := d.scannerMap.Load(typ); ok {
return v.(schema.ScannerFunc)
}
fn := scanner(typ)
if v, ok := d.scannerMap.LoadOrStore(typ, fn); ok {
return v.(schema.ScannerFunc)
}
return fn
}

View file

@ -0,0 +1,7 @@
module github.com/uptrace/bun/dialect/sqlitedialect
go 1.16
replace github.com/uptrace/bun => ../..
require github.com/uptrace/bun v0.4.3

View file

@ -0,0 +1,22 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo=
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
github.com/vmihailenco/msgpack/v5 v5.3.4 h1:qMKAwOV+meBw2Y8k9cVwAy7qErtYCwBzZ2ellBfvnqc=
github.com/vmihailenco/msgpack/v5 v5.3.4/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -0,0 +1,28 @@
package sqlitedialect
import (
"fmt"
"reflect"
"github.com/uptrace/bun/schema"
)
func scanner(typ reflect.Type) schema.ScannerFunc {
if typ.Kind() == reflect.Interface {
return scanInterface
}
return schema.Scanner(typ)
}
func scanInterface(dest reflect.Value, src interface{}) error {
if dest.IsNil() {
dest.Set(reflect.ValueOf(src))
return nil
}
dest = dest.Elem()
if fn := scanner(dest.Type()); fn != nil {
return fn(dest, src)
}
return fmt.Errorf("bun: can't scan %#v into %s", src, dest.Type())
}

27
vendor/golang.org/x/mod/LICENSE generated vendored Normal file
View file

@ -0,0 +1,27 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

22
vendor/golang.org/x/mod/PATENTS generated vendored Normal file
View file

@ -0,0 +1,22 @@
Additional IP Rights Grant (Patents)
"This implementation" means the copyrightable works distributed by
Google as part of the Go project.
Google hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section)
patent license to make, have made, use, offer to sell, sell, import,
transfer and otherwise run, modify and propagate the contents of this
implementation of Go, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of Go. This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation. If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of Go or any code incorporated within this
implementation of Go constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of Go
shall terminate as of the date such litigation is filed.

411
vendor/golang.org/x/mod/semver/semver.go generated vendored Normal file
View file

@ -0,0 +1,411 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package semver implements comparison of semantic version strings.
// In this package, semantic version strings must begin with a leading "v",
// as in "v1.0.0".
//
// The general form of a semantic version string accepted by this package is
//
// vMAJOR[.MINOR[.PATCH[-PRERELEASE][+BUILD]]]
//
// where square brackets indicate optional parts of the syntax;
// MAJOR, MINOR, and PATCH are decimal integers without extra leading zeros;
// PRERELEASE and BUILD are each a series of non-empty dot-separated identifiers
// using only alphanumeric characters and hyphens; and
// all-numeric PRERELEASE identifiers must not have leading zeros.
//
// This package follows Semantic Versioning 2.0.0 (see semver.org)
// with two exceptions. First, it requires the "v" prefix. Second, it recognizes
// vMAJOR and vMAJOR.MINOR (with no prerelease or build suffixes)
// as shorthands for vMAJOR.0.0 and vMAJOR.MINOR.0.
package semver
import "sort"
// parsed returns the parsed form of a semantic version string.
type parsed struct {
major string
minor string
patch string
short string
prerelease string
build string
err string
}
// IsValid reports whether v is a valid semantic version string.
func IsValid(v string) bool {
_, ok := parse(v)
return ok
}
// Canonical returns the canonical formatting of the semantic version v.
// It fills in any missing .MINOR or .PATCH and discards build metadata.
// Two semantic versions compare equal only if their canonical formattings
// are identical strings.
// The canonical invalid semantic version is the empty string.
func Canonical(v string) string {
p, ok := parse(v)
if !ok {
return ""
}
if p.build != "" {
return v[:len(v)-len(p.build)]
}
if p.short != "" {
return v + p.short
}
return v
}
// Major returns the major version prefix of the semantic version v.
// For example, Major("v2.1.0") == "v2".
// If v is an invalid semantic version string, Major returns the empty string.
func Major(v string) string {
pv, ok := parse(v)
if !ok {
return ""
}
return v[:1+len(pv.major)]
}
// MajorMinor returns the major.minor version prefix of the semantic version v.
// For example, MajorMinor("v2.1.0") == "v2.1".
// If v is an invalid semantic version string, MajorMinor returns the empty string.
func MajorMinor(v string) string {
pv, ok := parse(v)
if !ok {
return ""
}
i := 1 + len(pv.major)
if j := i + 1 + len(pv.minor); j <= len(v) && v[i] == '.' && v[i+1:j] == pv.minor {
return v[:j]
}
return v[:i] + "." + pv.minor
}
// Prerelease returns the prerelease suffix of the semantic version v.
// For example, Prerelease("v2.1.0-pre+meta") == "-pre".
// If v is an invalid semantic version string, Prerelease returns the empty string.
func Prerelease(v string) string {
pv, ok := parse(v)
if !ok {
return ""
}
return pv.prerelease
}
// Build returns the build suffix of the semantic version v.
// For example, Build("v2.1.0+meta") == "+meta".
// If v is an invalid semantic version string, Build returns the empty string.
func Build(v string) string {
pv, ok := parse(v)
if !ok {
return ""
}
return pv.build
}
// Compare returns an integer comparing two versions according to
// semantic version precedence.
// The result will be 0 if v == w, -1 if v < w, or +1 if v > w.
//
// An invalid semantic version string is considered less than a valid one.
// All invalid semantic version strings compare equal to each other.
func Compare(v, w string) int {
pv, ok1 := parse(v)
pw, ok2 := parse(w)
if !ok1 && !ok2 {
return 0
}
if !ok1 {
return -1
}
if !ok2 {
return +1
}
if c := compareInt(pv.major, pw.major); c != 0 {
return c
}
if c := compareInt(pv.minor, pw.minor); c != 0 {
return c
}
if c := compareInt(pv.patch, pw.patch); c != 0 {
return c
}
return comparePrerelease(pv.prerelease, pw.prerelease)
}
// Max canonicalizes its arguments and then returns the version string
// that compares greater.
//
// Deprecated: use Compare instead. In most cases, returning a canonicalized
// version is not expected or desired.
func Max(v, w string) string {
v = Canonical(v)
w = Canonical(w)
if Compare(v, w) > 0 {
return v
}
return w
}
// ByVersion implements sort.Interface for sorting semantic version strings.
type ByVersion []string
func (vs ByVersion) Len() int { return len(vs) }
func (vs ByVersion) Swap(i, j int) { vs[i], vs[j] = vs[j], vs[i] }
func (vs ByVersion) Less(i, j int) bool {
cmp := Compare(vs[i], vs[j])
if cmp != 0 {
return cmp < 0
}
return vs[i] < vs[j]
}
// Sort sorts a list of semantic version strings using ByVersion.
func Sort(list []string) {
sort.Sort(ByVersion(list))
}
func parse(v string) (p parsed, ok bool) {
if v == "" || v[0] != 'v' {
p.err = "missing v prefix"
return
}
p.major, v, ok = parseInt(v[1:])
if !ok {
p.err = "bad major version"
return
}
if v == "" {
p.minor = "0"
p.patch = "0"
p.short = ".0.0"
return
}
if v[0] != '.' {
p.err = "bad minor prefix"
ok = false
return
}
p.minor, v, ok = parseInt(v[1:])
if !ok {
p.err = "bad minor version"
return
}
if v == "" {
p.patch = "0"
p.short = ".0"
return
}
if v[0] != '.' {
p.err = "bad patch prefix"
ok = false
return
}
p.patch, v, ok = parseInt(v[1:])
if !ok {
p.err = "bad patch version"
return
}
if len(v) > 0 && v[0] == '-' {
p.prerelease, v, ok = parsePrerelease(v)
if !ok {
p.err = "bad prerelease"
return
}
}
if len(v) > 0 && v[0] == '+' {
p.build, v, ok = parseBuild(v)
if !ok {
p.err = "bad build"
return
}
}
if v != "" {
p.err = "junk on end"
ok = false
return
}
ok = true
return
}
func parseInt(v string) (t, rest string, ok bool) {
if v == "" {
return
}
if v[0] < '0' || '9' < v[0] {
return
}
i := 1
for i < len(v) && '0' <= v[i] && v[i] <= '9' {
i++
}
if v[0] == '0' && i != 1 {
return
}
return v[:i], v[i:], true
}
func parsePrerelease(v string) (t, rest string, ok bool) {
// "A pre-release version MAY be denoted by appending a hyphen and
// a series of dot separated identifiers immediately following the patch version.
// Identifiers MUST comprise only ASCII alphanumerics and hyphen [0-9A-Za-z-].
// Identifiers MUST NOT be empty. Numeric identifiers MUST NOT include leading zeroes."
if v == "" || v[0] != '-' {
return
}
i := 1
start := 1
for i < len(v) && v[i] != '+' {
if !isIdentChar(v[i]) && v[i] != '.' {
return
}
if v[i] == '.' {
if start == i || isBadNum(v[start:i]) {
return
}
start = i + 1
}
i++
}
if start == i || isBadNum(v[start:i]) {
return
}
return v[:i], v[i:], true
}
func parseBuild(v string) (t, rest string, ok bool) {
if v == "" || v[0] != '+' {
return
}
i := 1
start := 1
for i < len(v) {
if !isIdentChar(v[i]) && v[i] != '.' {
return
}
if v[i] == '.' {
if start == i {
return
}
start = i + 1
}
i++
}
if start == i {
return
}
return v[:i], v[i:], true
}
func isIdentChar(c byte) bool {
return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '-'
}
func isBadNum(v string) bool {
i := 0
for i < len(v) && '0' <= v[i] && v[i] <= '9' {
i++
}
return i == len(v) && i > 1 && v[0] == '0'
}
func isNum(v string) bool {
i := 0
for i < len(v) && '0' <= v[i] && v[i] <= '9' {
i++
}
return i == len(v)
}
func compareInt(x, y string) int {
if x == y {
return 0
}
if len(x) < len(y) {
return -1
}
if len(x) > len(y) {
return +1
}
if x < y {
return -1
} else {
return +1
}
}
func comparePrerelease(x, y string) int {
// "When major, minor, and patch are equal, a pre-release version has
// lower precedence than a normal version.
// Example: 1.0.0-alpha < 1.0.0.
// Precedence for two pre-release versions with the same major, minor,
// and patch version MUST be determined by comparing each dot separated
// identifier from left to right until a difference is found as follows:
// identifiers consisting of only digits are compared numerically and
// identifiers with letters or hyphens are compared lexically in ASCII
// sort order. Numeric identifiers always have lower precedence than
// non-numeric identifiers. A larger set of pre-release fields has a
// higher precedence than a smaller set, if all of the preceding
// identifiers are equal.
// Example: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta <
// 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0."
if x == y {
return 0
}
if x == "" {
return +1
}
if y == "" {
return -1
}
for x != "" && y != "" {
x = x[1:] // skip - or .
y = y[1:] // skip - or .
var dx, dy string
dx, x = nextIdent(x)
dy, y = nextIdent(y)
if dx != dy {
ix := isNum(dx)
iy := isNum(dy)
if ix != iy {
if ix {
return -1
} else {
return +1
}
}
if ix {
if len(dx) < len(dy) {
return -1
}
if len(dx) > len(dy) {
return +1
}
}
if dx < dy {
return -1
} else {
return +1
}
}
}
if x == "" {
return -1
} else {
return +1
}
}
func nextIdent(x string) (dx, rest string) {
i := 0
for i < len(x) && x[i] != '.' {
i++
}
return x[:i], x[i:]
}

1
vendor/golang.org/x/sys/cpu/cpu.go generated vendored
View file

@ -56,6 +56,7 @@ var X86 struct {
HasAVX512BF16 bool // Advanced vector extension 512 BFloat16 Instructions HasAVX512BF16 bool // Advanced vector extension 512 BFloat16 Instructions
HasBMI1 bool // Bit manipulation instruction set 1 HasBMI1 bool // Bit manipulation instruction set 1
HasBMI2 bool // Bit manipulation instruction set 2 HasBMI2 bool // Bit manipulation instruction set 2
HasCX16 bool // Compare and exchange 16 Bytes
HasERMS bool // Enhanced REP for MOVSB and STOSB HasERMS bool // Enhanced REP for MOVSB and STOSB
HasFMA bool // Fused-multiply-add instructions HasFMA bool // Fused-multiply-add instructions
HasOSXSAVE bool // OS supports XSAVE/XRESTOR for saving/restoring XMM registers. HasOSXSAVE bool // OS supports XSAVE/XRESTOR for saving/restoring XMM registers.

View file

@ -39,6 +39,7 @@ func initOptions() {
{Name: "avx512bf16", Feature: &X86.HasAVX512BF16}, {Name: "avx512bf16", Feature: &X86.HasAVX512BF16},
{Name: "bmi1", Feature: &X86.HasBMI1}, {Name: "bmi1", Feature: &X86.HasBMI1},
{Name: "bmi2", Feature: &X86.HasBMI2}, {Name: "bmi2", Feature: &X86.HasBMI2},
{Name: "cx16", Feature: &X86.HasCX16},
{Name: "erms", Feature: &X86.HasERMS}, {Name: "erms", Feature: &X86.HasERMS},
{Name: "fma", Feature: &X86.HasFMA}, {Name: "fma", Feature: &X86.HasFMA},
{Name: "osxsave", Feature: &X86.HasOSXSAVE}, {Name: "osxsave", Feature: &X86.HasOSXSAVE},
@ -73,6 +74,7 @@ func archInit() {
X86.HasPCLMULQDQ = isSet(1, ecx1) X86.HasPCLMULQDQ = isSet(1, ecx1)
X86.HasSSSE3 = isSet(9, ecx1) X86.HasSSSE3 = isSet(9, ecx1)
X86.HasFMA = isSet(12, ecx1) X86.HasFMA = isSet(12, ecx1)
X86.HasCX16 = isSet(13, ecx1)
X86.HasSSE41 = isSet(19, ecx1) X86.HasSSE41 = isSet(19, ecx1)
X86.HasSSE42 = isSet(20, ecx1) X86.HasSSE42 = isSet(20, ecx1)
X86.HasPOPCNT = isSet(23, ecx1) X86.HasPOPCNT = isSet(23, ecx1)

102
vendor/golang.org/x/sys/execabs/execabs.go generated vendored Normal file
View file

@ -0,0 +1,102 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package execabs is a drop-in replacement for os/exec
// that requires PATH lookups to find absolute paths.
// That is, execabs.Command("cmd") runs the same PATH lookup
// as exec.Command("cmd"), but if the result is a path
// which is relative, the Run and Start methods will report
// an error instead of running the executable.
//
// See https://blog.golang.org/path-security for more information
// about when it may be necessary or appropriate to use this package.
package execabs
import (
"context"
"fmt"
"os/exec"
"path/filepath"
"reflect"
"unsafe"
)
// ErrNotFound is the error resulting if a path search failed to find an executable file.
// It is an alias for exec.ErrNotFound.
var ErrNotFound = exec.ErrNotFound
// Cmd represents an external command being prepared or run.
// It is an alias for exec.Cmd.
type Cmd = exec.Cmd
// Error is returned by LookPath when it fails to classify a file as an executable.
// It is an alias for exec.Error.
type Error = exec.Error
// An ExitError reports an unsuccessful exit by a command.
// It is an alias for exec.ExitError.
type ExitError = exec.ExitError
func relError(file, path string) error {
return fmt.Errorf("%s resolves to executable in current directory (.%c%s)", file, filepath.Separator, path)
}
// LookPath searches for an executable named file in the directories
// named by the PATH environment variable. If file contains a slash,
// it is tried directly and the PATH is not consulted. The result will be
// an absolute path.
//
// LookPath differs from exec.LookPath in its handling of PATH lookups,
// which are used for file names without slashes. If exec.LookPath's
// PATH lookup would have returned an executable from the current directory,
// LookPath instead returns an error.
func LookPath(file string) (string, error) {
path, err := exec.LookPath(file)
if err != nil {
return "", err
}
if filepath.Base(file) == file && !filepath.IsAbs(path) {
return "", relError(file, path)
}
return path, nil
}
func fixCmd(name string, cmd *exec.Cmd) {
if filepath.Base(name) == name && !filepath.IsAbs(cmd.Path) {
// exec.Command was called with a bare binary name and
// exec.LookPath returned a path which is not absolute.
// Set cmd.lookPathErr and clear cmd.Path so that it
// cannot be run.
lookPathErr := (*error)(unsafe.Pointer(reflect.ValueOf(cmd).Elem().FieldByName("lookPathErr").Addr().Pointer()))
if *lookPathErr == nil {
*lookPathErr = relError(name, cmd.Path)
}
cmd.Path = ""
}
}
// CommandContext is like Command but includes a context.
//
// The provided context is used to kill the process (by calling os.Process.Kill)
// if the context becomes done before the command completes on its own.
func CommandContext(ctx context.Context, name string, arg ...string) *exec.Cmd {
cmd := exec.CommandContext(ctx, name, arg...)
fixCmd(name, cmd)
return cmd
}
// Command returns the Cmd struct to execute the named program with the given arguments.
// See exec.Command for most details.
//
// Command differs from exec.Command in its handling of PATH lookups,
// which are used when the program name contains no slashes.
// If exec.Command would have returned an exec.Cmd configured to run an
// executable from the current directory, Command instead
// returns an exec.Cmd that will return an error from Start or Run.
func Command(name string, arg ...string) *exec.Cmd {
cmd := exec.Command(name, arg...)
fixCmd(name, cmd)
return cmd
}

149
vendor/golang.org/x/sys/unix/ifreq_linux.go generated vendored Normal file
View file

@ -0,0 +1,149 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build linux
// +build linux
package unix
import (
"bytes"
"unsafe"
)
// Helpers for dealing with ifreq since it contains a union and thus requires a
// lot of unsafe.Pointer casts to use properly.
// An Ifreq is a type-safe wrapper around the raw ifreq struct. An Ifreq
// contains an interface name and a union of arbitrary data which can be
// accessed using the Ifreq's methods. To create an Ifreq, use the NewIfreq
// function.
//
// Use the Name method to access the stored interface name. The union data
// fields can be get and set using the following methods:
// - Uint16/SetUint16: flags
// - Uint32/SetUint32: ifindex, metric, mtu
type Ifreq struct{ raw ifreq }
// NewIfreq creates an Ifreq with the input network interface name after
// validating the name does not exceed IFNAMSIZ-1 (trailing NULL required)
// bytes.
func NewIfreq(name string) (*Ifreq, error) {
// Leave room for terminating NULL byte.
if len(name) >= IFNAMSIZ {
return nil, EINVAL
}
var ifr ifreq
copy(ifr.Ifrn[:], name)
return &Ifreq{raw: ifr}, nil
}
// TODO(mdlayher): get/set methods for hardware address sockaddr, char array, etc.
// Name returns the interface name associated with the Ifreq.
func (ifr *Ifreq) Name() string {
// BytePtrToString requires a NULL terminator or the program may crash. If
// one is not present, just return the empty string.
if !bytes.Contains(ifr.raw.Ifrn[:], []byte{0x00}) {
return ""
}
return BytePtrToString(&ifr.raw.Ifrn[0])
}
// According to netdevice(7), only AF_INET addresses are returned for numerous
// sockaddr ioctls. For convenience, we expose these as Inet4Addr since the Port
// field and other data is always empty.
// Inet4Addr returns the Ifreq union data from an embedded sockaddr as a C
// in_addr/Go []byte (4-byte IPv4 address) value. If the sockaddr family is not
// AF_INET, an error is returned.
func (ifr *Ifreq) Inet4Addr() ([]byte, error) {
raw := *(*RawSockaddrInet4)(unsafe.Pointer(&ifr.raw.Ifru[:SizeofSockaddrInet4][0]))
if raw.Family != AF_INET {
// Cannot safely interpret raw.Addr bytes as an IPv4 address.
return nil, EINVAL
}
return raw.Addr[:], nil
}
// SetInet4Addr sets a C in_addr/Go []byte (4-byte IPv4 address) value in an
// embedded sockaddr within the Ifreq's union data. v must be 4 bytes in length
// or an error will be returned.
func (ifr *Ifreq) SetInet4Addr(v []byte) error {
if len(v) != 4 {
return EINVAL
}
var addr [4]byte
copy(addr[:], v)
ifr.clear()
*(*RawSockaddrInet4)(
unsafe.Pointer(&ifr.raw.Ifru[:SizeofSockaddrInet4][0]),
) = RawSockaddrInet4{
// Always set IP family as ioctls would require it anyway.
Family: AF_INET,
Addr: addr,
}
return nil
}
// Uint16 returns the Ifreq union data as a C short/Go uint16 value.
func (ifr *Ifreq) Uint16() uint16 {
return *(*uint16)(unsafe.Pointer(&ifr.raw.Ifru[:2][0]))
}
// SetUint16 sets a C short/Go uint16 value as the Ifreq's union data.
func (ifr *Ifreq) SetUint16(v uint16) {
ifr.clear()
*(*uint16)(unsafe.Pointer(&ifr.raw.Ifru[:2][0])) = v
}
// Uint32 returns the Ifreq union data as a C int/Go uint32 value.
func (ifr *Ifreq) Uint32() uint32 {
return *(*uint32)(unsafe.Pointer(&ifr.raw.Ifru[:4][0]))
}
// SetUint32 sets a C int/Go uint32 value as the Ifreq's union data.
func (ifr *Ifreq) SetUint32(v uint32) {
ifr.clear()
*(*uint32)(unsafe.Pointer(&ifr.raw.Ifru[:4][0])) = v
}
// clear zeroes the ifreq's union field to prevent trailing garbage data from
// being sent to the kernel if an ifreq is reused.
func (ifr *Ifreq) clear() {
for i := range ifr.raw.Ifru {
ifr.raw.Ifru[i] = 0
}
}
// TODO(mdlayher): export as IfreqData? For now we can provide helpers such as
// IoctlGetEthtoolDrvinfo which use these APIs under the hood.
// An ifreqData is an Ifreq which carries pointer data. To produce an ifreqData,
// use the Ifreq.withData method.
type ifreqData struct {
name [IFNAMSIZ]byte
// A type separate from ifreq is required in order to comply with the
// unsafe.Pointer rules since the "pointer-ness" of data would not be
// preserved if it were cast into the byte array of a raw ifreq.
data unsafe.Pointer
// Pad to the same size as ifreq.
_ [len(ifreq{}.Ifru) - SizeofPtr]byte
}
// withData produces an ifreqData with the pointer p set for ioctls which require
// arbitrary pointer data.
func (ifr Ifreq) withData(p unsafe.Pointer) ifreqData {
return ifreqData{
name: ifr.raw.Ifrn,
data: p,
}
}

View file

@ -5,7 +5,6 @@
package unix package unix
import ( import (
"runtime"
"unsafe" "unsafe"
) )
@ -22,56 +21,42 @@ func IoctlRetInt(fd int, req uint) (int, error) {
func IoctlGetUint32(fd int, req uint) (uint32, error) { func IoctlGetUint32(fd int, req uint) (uint32, error) {
var value uint32 var value uint32
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value))) err := ioctlPtr(fd, req, unsafe.Pointer(&value))
return value, err return value, err
} }
func IoctlGetRTCTime(fd int) (*RTCTime, error) { func IoctlGetRTCTime(fd int) (*RTCTime, error) {
var value RTCTime var value RTCTime
err := ioctl(fd, RTC_RD_TIME, uintptr(unsafe.Pointer(&value))) err := ioctlPtr(fd, RTC_RD_TIME, unsafe.Pointer(&value))
return &value, err return &value, err
} }
func IoctlSetRTCTime(fd int, value *RTCTime) error { func IoctlSetRTCTime(fd int, value *RTCTime) error {
err := ioctl(fd, RTC_SET_TIME, uintptr(unsafe.Pointer(value))) return ioctlPtr(fd, RTC_SET_TIME, unsafe.Pointer(value))
runtime.KeepAlive(value)
return err
} }
func IoctlGetRTCWkAlrm(fd int) (*RTCWkAlrm, error) { func IoctlGetRTCWkAlrm(fd int) (*RTCWkAlrm, error) {
var value RTCWkAlrm var value RTCWkAlrm
err := ioctl(fd, RTC_WKALM_RD, uintptr(unsafe.Pointer(&value))) err := ioctlPtr(fd, RTC_WKALM_RD, unsafe.Pointer(&value))
return &value, err return &value, err
} }
func IoctlSetRTCWkAlrm(fd int, value *RTCWkAlrm) error { func IoctlSetRTCWkAlrm(fd int, value *RTCWkAlrm) error {
err := ioctl(fd, RTC_WKALM_SET, uintptr(unsafe.Pointer(value))) return ioctlPtr(fd, RTC_WKALM_SET, unsafe.Pointer(value))
runtime.KeepAlive(value)
return err
}
type ifreqEthtool struct {
name [IFNAMSIZ]byte
data unsafe.Pointer
} }
// IoctlGetEthtoolDrvinfo fetches ethtool driver information for the network // IoctlGetEthtoolDrvinfo fetches ethtool driver information for the network
// device specified by ifname. // device specified by ifname.
func IoctlGetEthtoolDrvinfo(fd int, ifname string) (*EthtoolDrvinfo, error) { func IoctlGetEthtoolDrvinfo(fd int, ifname string) (*EthtoolDrvinfo, error) {
// Leave room for terminating NULL byte. ifr, err := NewIfreq(ifname)
if len(ifname) >= IFNAMSIZ { if err != nil {
return nil, EINVAL return nil, err
} }
value := EthtoolDrvinfo{ value := EthtoolDrvinfo{Cmd: ETHTOOL_GDRVINFO}
Cmd: ETHTOOL_GDRVINFO, ifrd := ifr.withData(unsafe.Pointer(&value))
}
ifreq := ifreqEthtool{ err = ioctlIfreqData(fd, SIOCETHTOOL, &ifrd)
data: unsafe.Pointer(&value),
}
copy(ifreq.name[:], ifname)
err := ioctl(fd, SIOCETHTOOL, uintptr(unsafe.Pointer(&ifreq)))
runtime.KeepAlive(ifreq)
return &value, err return &value, err
} }
@ -80,7 +65,7 @@ func IoctlGetEthtoolDrvinfo(fd int, ifname string) (*EthtoolDrvinfo, error) {
// https://www.kernel.org/doc/html/latest/watchdog/watchdog-api.html. // https://www.kernel.org/doc/html/latest/watchdog/watchdog-api.html.
func IoctlGetWatchdogInfo(fd int) (*WatchdogInfo, error) { func IoctlGetWatchdogInfo(fd int) (*WatchdogInfo, error) {
var value WatchdogInfo var value WatchdogInfo
err := ioctl(fd, WDIOC_GETSUPPORT, uintptr(unsafe.Pointer(&value))) err := ioctlPtr(fd, WDIOC_GETSUPPORT, unsafe.Pointer(&value))
return &value, err return &value, err
} }
@ -88,6 +73,7 @@ func IoctlGetWatchdogInfo(fd int) (*WatchdogInfo, error) {
// more information, see: // more information, see:
// https://www.kernel.org/doc/html/latest/watchdog/watchdog-api.html. // https://www.kernel.org/doc/html/latest/watchdog/watchdog-api.html.
func IoctlWatchdogKeepalive(fd int) error { func IoctlWatchdogKeepalive(fd int) error {
// arg is ignored and not a pointer, so ioctl is fine instead of ioctlPtr.
return ioctl(fd, WDIOC_KEEPALIVE, 0) return ioctl(fd, WDIOC_KEEPALIVE, 0)
} }
@ -95,9 +81,7 @@ func IoctlWatchdogKeepalive(fd int) error {
// range of data conveyed in value to the file associated with the file // range of data conveyed in value to the file associated with the file
// descriptor destFd. See the ioctl_ficlonerange(2) man page for details. // descriptor destFd. See the ioctl_ficlonerange(2) man page for details.
func IoctlFileCloneRange(destFd int, value *FileCloneRange) error { func IoctlFileCloneRange(destFd int, value *FileCloneRange) error {
err := ioctl(destFd, FICLONERANGE, uintptr(unsafe.Pointer(value))) return ioctlPtr(destFd, FICLONERANGE, unsafe.Pointer(value))
runtime.KeepAlive(value)
return err
} }
// IoctlFileClone performs an FICLONE ioctl operation to clone the entire file // IoctlFileClone performs an FICLONE ioctl operation to clone the entire file
@ -148,7 +132,7 @@ func IoctlFileDedupeRange(srcFd int, value *FileDedupeRange) error {
rawinfo.Reserved = value.Info[i].Reserved rawinfo.Reserved = value.Info[i].Reserved
} }
err := ioctl(srcFd, FIDEDUPERANGE, uintptr(unsafe.Pointer(&buf[0]))) err := ioctlPtr(srcFd, FIDEDUPERANGE, unsafe.Pointer(&buf[0]))
// Output // Output
for i := range value.Info { for i := range value.Info {
@ -166,31 +150,47 @@ func IoctlFileDedupeRange(srcFd int, value *FileDedupeRange) error {
} }
func IoctlHIDGetDesc(fd int, value *HIDRawReportDescriptor) error { func IoctlHIDGetDesc(fd int, value *HIDRawReportDescriptor) error {
err := ioctl(fd, HIDIOCGRDESC, uintptr(unsafe.Pointer(value))) return ioctlPtr(fd, HIDIOCGRDESC, unsafe.Pointer(value))
runtime.KeepAlive(value)
return err
} }
func IoctlHIDGetRawInfo(fd int) (*HIDRawDevInfo, error) { func IoctlHIDGetRawInfo(fd int) (*HIDRawDevInfo, error) {
var value HIDRawDevInfo var value HIDRawDevInfo
err := ioctl(fd, HIDIOCGRAWINFO, uintptr(unsafe.Pointer(&value))) err := ioctlPtr(fd, HIDIOCGRAWINFO, unsafe.Pointer(&value))
return &value, err return &value, err
} }
func IoctlHIDGetRawName(fd int) (string, error) { func IoctlHIDGetRawName(fd int) (string, error) {
var value [_HIDIOCGRAWNAME_LEN]byte var value [_HIDIOCGRAWNAME_LEN]byte
err := ioctl(fd, _HIDIOCGRAWNAME, uintptr(unsafe.Pointer(&value[0]))) err := ioctlPtr(fd, _HIDIOCGRAWNAME, unsafe.Pointer(&value[0]))
return ByteSliceToString(value[:]), err return ByteSliceToString(value[:]), err
} }
func IoctlHIDGetRawPhys(fd int) (string, error) { func IoctlHIDGetRawPhys(fd int) (string, error) {
var value [_HIDIOCGRAWPHYS_LEN]byte var value [_HIDIOCGRAWPHYS_LEN]byte
err := ioctl(fd, _HIDIOCGRAWPHYS, uintptr(unsafe.Pointer(&value[0]))) err := ioctlPtr(fd, _HIDIOCGRAWPHYS, unsafe.Pointer(&value[0]))
return ByteSliceToString(value[:]), err return ByteSliceToString(value[:]), err
} }
func IoctlHIDGetRawUniq(fd int) (string, error) { func IoctlHIDGetRawUniq(fd int) (string, error) {
var value [_HIDIOCGRAWUNIQ_LEN]byte var value [_HIDIOCGRAWUNIQ_LEN]byte
err := ioctl(fd, _HIDIOCGRAWUNIQ, uintptr(unsafe.Pointer(&value[0]))) err := ioctlPtr(fd, _HIDIOCGRAWUNIQ, unsafe.Pointer(&value[0]))
return ByteSliceToString(value[:]), err return ByteSliceToString(value[:]), err
} }
// IoctlIfreq performs an ioctl using an Ifreq structure for input and/or
// output. See the netdevice(7) man page for details.
func IoctlIfreq(fd int, req uint, value *Ifreq) error {
// It is possible we will add more fields to *Ifreq itself later to prevent
// misuse, so pass the raw *ifreq directly.
return ioctlPtr(fd, req, unsafe.Pointer(&value.raw))
}
// TODO(mdlayher): export if and when IfreqData is exported.
// ioctlIfreqData performs an ioctl using an ifreqData structure for input
// and/or output. See the netdevice(7) man page for details.
func ioctlIfreqData(fd int, req uint, value *ifreqData) error {
// The memory layout of IfreqData (type-safe) and ifreq (not type-safe) are
// identical so pass *IfreqData directly.
return ioctlPtr(fd, req, unsafe.Pointer(value))
}

View file

@ -217,8 +217,6 @@ struct ltchars {
#include <linux/genetlink.h> #include <linux/genetlink.h>
#include <linux/hdreg.h> #include <linux/hdreg.h>
#include <linux/hidraw.h> #include <linux/hidraw.h>
#include <linux/icmp.h>
#include <linux/icmpv6.h>
#include <linux/if.h> #include <linux/if.h>
#include <linux/if_addr.h> #include <linux/if_addr.h>
#include <linux/if_alg.h> #include <linux/if_alg.h>
@ -502,7 +500,7 @@ ccflags="$@"
$2 ~ /^LOCK_(SH|EX|NB|UN)$/ || $2 ~ /^LOCK_(SH|EX|NB|UN)$/ ||
$2 ~ /^LO_(KEY|NAME)_SIZE$/ || $2 ~ /^LO_(KEY|NAME)_SIZE$/ ||
$2 ~ /^LOOP_(CLR|CTL|GET|SET)_/ || $2 ~ /^LOOP_(CLR|CTL|GET|SET)_/ ||
$2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|TCP|MCAST|EVFILT|NOTE|SHUT|PROT|MAP|MFD|T?PACKET|MSG|SCM|MCL|DT|MADV|PR|LOCAL)_/ || $2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|TCP|MCAST|EVFILT|NOTE|SHUT|PROT|MAP|MFD|T?PACKET|MSG|SCM|MCL|DT|MADV|PR|LOCAL|TCPOPT)_/ ||
$2 ~ /^NFC_(GENL|PROTO|COMM|RF|SE|DIRECTION|LLCP|SOCKPROTO)_/ || $2 ~ /^NFC_(GENL|PROTO|COMM|RF|SE|DIRECTION|LLCP|SOCKPROTO)_/ ||
$2 ~ /^NFC_.*_(MAX)?SIZE$/ || $2 ~ /^NFC_.*_(MAX)?SIZE$/ ||
$2 ~ /^RAW_PAYLOAD_/ || $2 ~ /^RAW_PAYLOAD_/ ||

View file

@ -66,11 +66,18 @@ func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) {
return fchmodat(dirfd, path, mode) return fchmodat(dirfd, path, mode)
} }
//sys ioctl(fd int, req uint, arg uintptr) (err error) //sys ioctl(fd int, req uint, arg uintptr) (err error) = SYS_IOCTL
//sys ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) = SYS_IOCTL
// ioctl itself should not be exposed directly, but additional get/set // ioctl itself should not be exposed directly, but additional get/set functions
// functions for specific types are permissible. // for specific types are permissible. These are defined in ioctl.go and
// These are defined in ioctl.go and ioctl_linux.go. // ioctl_linux.go.
//
// The third argument to ioctl is often a pointer but sometimes an integer.
// Callers should use ioctlPtr when the third argument is a pointer and ioctl
// when the third argument is an integer.
//
// TODO: some existing code incorrectly uses ioctl when it should use ioctlPtr.
//sys Linkat(olddirfd int, oldpath string, newdirfd int, newpath string, flags int) (err error) //sys Linkat(olddirfd int, oldpath string, newdirfd int, newpath string, flags int) (err error)
@ -1348,6 +1355,13 @@ func SetsockoptTpacketReq3(fd, level, opt int, tp *TpacketReq3) error {
return setsockopt(fd, level, opt, unsafe.Pointer(tp), unsafe.Sizeof(*tp)) return setsockopt(fd, level, opt, unsafe.Pointer(tp), unsafe.Sizeof(*tp))
} }
func SetsockoptTCPRepairOpt(fd, level, opt int, o []TCPRepairOpt) (err error) {
if len(o) == 0 {
return EINVAL
}
return setsockopt(fd, level, opt, unsafe.Pointer(&o[0]), uintptr(SizeofTCPRepairOpt*len(o)))
}
// Keyctl Commands (http://man7.org/linux/man-pages/man2/keyctl.2.html) // Keyctl Commands (http://man7.org/linux/man-pages/man2/keyctl.2.html)
// KeyctlInt calls keyctl commands in which each argument is an int. // KeyctlInt calls keyctl commands in which each argument is an int.
@ -1859,7 +1873,7 @@ func Getpgrp() (pid int) {
//sys Nanosleep(time *Timespec, leftover *Timespec) (err error) //sys Nanosleep(time *Timespec, leftover *Timespec) (err error)
//sys PerfEventOpen(attr *PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error) //sys PerfEventOpen(attr *PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error)
//sys PivotRoot(newroot string, putold string) (err error) = SYS_PIVOT_ROOT //sys PivotRoot(newroot string, putold string) (err error) = SYS_PIVOT_ROOT
//sysnb prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) = SYS_PRLIMIT64 //sysnb Prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) = SYS_PRLIMIT64
//sys Prctl(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) (err error) //sys Prctl(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) (err error)
//sys Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error) = SYS_PSELECT6 //sys Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error) = SYS_PSELECT6
//sys read(fd int, p []byte) (n int, err error) //sys read(fd int, p []byte) (n int, err error)

View file

@ -105,7 +105,7 @@ const rlimInf32 = ^uint32(0)
const rlimInf64 = ^uint64(0) const rlimInf64 = ^uint64(0)
func Getrlimit(resource int, rlim *Rlimit) (err error) { func Getrlimit(resource int, rlim *Rlimit) (err error) {
err = prlimit(0, resource, nil, rlim) err = Prlimit(0, resource, nil, rlim)
if err != ENOSYS { if err != ENOSYS {
return err return err
} }
@ -133,7 +133,7 @@ func Getrlimit(resource int, rlim *Rlimit) (err error) {
//sysnb setrlimit(resource int, rlim *rlimit32) (err error) = SYS_SETRLIMIT //sysnb setrlimit(resource int, rlim *rlimit32) (err error) = SYS_SETRLIMIT
func Setrlimit(resource int, rlim *Rlimit) (err error) { func Setrlimit(resource int, rlim *Rlimit) (err error) {
err = prlimit(0, resource, rlim, nil) err = Prlimit(0, resource, rlim, nil)
if err != ENOSYS { if err != ENOSYS {
return err return err
} }

View file

@ -184,7 +184,7 @@ const rlimInf32 = ^uint32(0)
const rlimInf64 = ^uint64(0) const rlimInf64 = ^uint64(0)
func Getrlimit(resource int, rlim *Rlimit) (err error) { func Getrlimit(resource int, rlim *Rlimit) (err error) {
err = prlimit(0, resource, nil, rlim) err = Prlimit(0, resource, nil, rlim)
if err != ENOSYS { if err != ENOSYS {
return err return err
} }
@ -212,7 +212,7 @@ func Getrlimit(resource int, rlim *Rlimit) (err error) {
//sysnb setrlimit(resource int, rlim *rlimit32) (err error) = SYS_SETRLIMIT //sysnb setrlimit(resource int, rlim *rlimit32) (err error) = SYS_SETRLIMIT
func Setrlimit(resource int, rlim *Rlimit) (err error) { func Setrlimit(resource int, rlim *Rlimit) (err error) {
err = prlimit(0, resource, rlim, nil) err = Prlimit(0, resource, rlim, nil)
if err != ENOSYS { if err != ENOSYS {
return err return err
} }

View file

@ -171,7 +171,7 @@ func Pipe2(p []int, flags int) (err error) {
// Getrlimit prefers the prlimit64 system call. See issue 38604. // Getrlimit prefers the prlimit64 system call. See issue 38604.
func Getrlimit(resource int, rlim *Rlimit) error { func Getrlimit(resource int, rlim *Rlimit) error {
err := prlimit(0, resource, nil, rlim) err := Prlimit(0, resource, nil, rlim)
if err != ENOSYS { if err != ENOSYS {
return err return err
} }
@ -180,7 +180,7 @@ func Getrlimit(resource int, rlim *Rlimit) error {
// Setrlimit prefers the prlimit64 system call. See issue 38604. // Setrlimit prefers the prlimit64 system call. See issue 38604.
func Setrlimit(resource int, rlim *Rlimit) error { func Setrlimit(resource int, rlim *Rlimit) error {
err := prlimit(0, resource, rlim, nil) err := Prlimit(0, resource, rlim, nil)
if err != ENOSYS { if err != ENOSYS {
return err return err
} }

View file

@ -157,7 +157,7 @@ type rlimit32 struct {
//sysnb getrlimit(resource int, rlim *rlimit32) (err error) = SYS_GETRLIMIT //sysnb getrlimit(resource int, rlim *rlimit32) (err error) = SYS_GETRLIMIT
func Getrlimit(resource int, rlim *Rlimit) (err error) { func Getrlimit(resource int, rlim *Rlimit) (err error) {
err = prlimit(0, resource, nil, rlim) err = Prlimit(0, resource, nil, rlim)
if err != ENOSYS { if err != ENOSYS {
return err return err
} }
@ -185,7 +185,7 @@ func Getrlimit(resource int, rlim *Rlimit) (err error) {
//sysnb setrlimit(resource int, rlim *rlimit32) (err error) = SYS_SETRLIMIT //sysnb setrlimit(resource int, rlim *rlimit32) (err error) = SYS_SETRLIMIT
func Setrlimit(resource int, rlim *Rlimit) (err error) { func Setrlimit(resource int, rlim *Rlimit) (err error) {
err = prlimit(0, resource, rlim, nil) err = Prlimit(0, resource, rlim, nil)
if err != ENOSYS { if err != ENOSYS {
return err return err
} }

View file

@ -3,8 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build linux && ppc //go:build linux && ppc
// +build linux // +build linux,ppc
// +build ppc
package unix package unix
@ -143,7 +142,7 @@ const rlimInf32 = ^uint32(0)
const rlimInf64 = ^uint64(0) const rlimInf64 = ^uint64(0)
func Getrlimit(resource int, rlim *Rlimit) (err error) { func Getrlimit(resource int, rlim *Rlimit) (err error) {
err = prlimit(0, resource, nil, rlim) err = Prlimit(0, resource, nil, rlim)
if err != ENOSYS { if err != ENOSYS {
return err return err
} }
@ -171,7 +170,7 @@ func Getrlimit(resource int, rlim *Rlimit) (err error) {
//sysnb setrlimit(resource int, rlim *rlimit32) (err error) = SYS_SETRLIMIT //sysnb setrlimit(resource int, rlim *rlimit32) (err error) = SYS_SETRLIMIT
func Setrlimit(resource int, rlim *Rlimit) (err error) { func Setrlimit(resource int, rlim *Rlimit) (err error) {
err = prlimit(0, resource, rlim, nil) err = Prlimit(0, resource, rlim, nil)
if err != ENOSYS { if err != ENOSYS {
return err return err
} }

View file

@ -13,7 +13,10 @@
package unix package unix
import ( import (
"fmt"
"os"
"runtime" "runtime"
"sync"
"syscall" "syscall"
"unsafe" "unsafe"
) )
@ -744,3 +747,240 @@ func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, e
func Munmap(b []byte) (err error) { func Munmap(b []byte) (err error) {
return mapper.Munmap(b) return mapper.Munmap(b)
} }
// Event Ports
type fileObjCookie struct {
fobj *fileObj
cookie interface{}
}
// EventPort provides a safe abstraction on top of Solaris/illumos Event Ports.
type EventPort struct {
port int
mu sync.Mutex
fds map[uintptr]interface{}
paths map[string]*fileObjCookie
}
// PortEvent is an abstraction of the port_event C struct.
// Compare Source against PORT_SOURCE_FILE or PORT_SOURCE_FD
// to see if Path or Fd was the event source. The other will be
// uninitialized.
type PortEvent struct {
Cookie interface{}
Events int32
Fd uintptr
Path string
Source uint16
fobj *fileObj
}
// NewEventPort creates a new EventPort including the
// underlying call to port_create(3c).
func NewEventPort() (*EventPort, error) {
port, err := port_create()
if err != nil {
return nil, err
}
e := &EventPort{
port: port,
fds: make(map[uintptr]interface{}),
paths: make(map[string]*fileObjCookie),
}
return e, nil
}
//sys port_create() (n int, err error)
//sys port_associate(port int, source int, object uintptr, events int, user *byte) (n int, err error)
//sys port_dissociate(port int, source int, object uintptr) (n int, err error)
//sys port_get(port int, pe *portEvent, timeout *Timespec) (n int, err error)
//sys port_getn(port int, pe *portEvent, max uint32, nget *uint32, timeout *Timespec) (n int, err error)
// Close closes the event port.
func (e *EventPort) Close() error {
e.mu.Lock()
defer e.mu.Unlock()
e.fds = nil
e.paths = nil
return Close(e.port)
}
// PathIsWatched checks to see if path is associated with this EventPort.
func (e *EventPort) PathIsWatched(path string) bool {
e.mu.Lock()
defer e.mu.Unlock()
_, found := e.paths[path]
return found
}
// FdIsWatched checks to see if fd is associated with this EventPort.
func (e *EventPort) FdIsWatched(fd uintptr) bool {
e.mu.Lock()
defer e.mu.Unlock()
_, found := e.fds[fd]
return found
}
// AssociatePath wraps port_associate(3c) for a filesystem path including
// creating the necessary file_obj from the provided stat information.
func (e *EventPort) AssociatePath(path string, stat os.FileInfo, events int, cookie interface{}) error {
e.mu.Lock()
defer e.mu.Unlock()
if _, found := e.paths[path]; found {
return fmt.Errorf("%v is already associated with this Event Port", path)
}
fobj, err := createFileObj(path, stat)
if err != nil {
return err
}
fCookie := &fileObjCookie{fobj, cookie}
_, err = port_associate(e.port, PORT_SOURCE_FILE, uintptr(unsafe.Pointer(fobj)), events, (*byte)(unsafe.Pointer(&fCookie.cookie)))
if err != nil {
return err
}
e.paths[path] = fCookie
return nil
}
// DissociatePath wraps port_dissociate(3c) for a filesystem path.
func (e *EventPort) DissociatePath(path string) error {
e.mu.Lock()
defer e.mu.Unlock()
f, ok := e.paths[path]
if !ok {
return fmt.Errorf("%v is not associated with this Event Port", path)
}
_, err := port_dissociate(e.port, PORT_SOURCE_FILE, uintptr(unsafe.Pointer(f.fobj)))
if err != nil {
return err
}
delete(e.paths, path)
return nil
}
// AssociateFd wraps calls to port_associate(3c) on file descriptors.
func (e *EventPort) AssociateFd(fd uintptr, events int, cookie interface{}) error {
e.mu.Lock()
defer e.mu.Unlock()
if _, found := e.fds[fd]; found {
return fmt.Errorf("%v is already associated with this Event Port", fd)
}
pcookie := &cookie
_, err := port_associate(e.port, PORT_SOURCE_FD, fd, events, (*byte)(unsafe.Pointer(pcookie)))
if err != nil {
return err
}
e.fds[fd] = pcookie
return nil
}
// DissociateFd wraps calls to port_dissociate(3c) on file descriptors.
func (e *EventPort) DissociateFd(fd uintptr) error {
e.mu.Lock()
defer e.mu.Unlock()
_, ok := e.fds[fd]
if !ok {
return fmt.Errorf("%v is not associated with this Event Port", fd)
}
_, err := port_dissociate(e.port, PORT_SOURCE_FD, fd)
if err != nil {
return err
}
delete(e.fds, fd)
return nil
}
func createFileObj(name string, stat os.FileInfo) (*fileObj, error) {
fobj := new(fileObj)
bs, err := ByteSliceFromString(name)
if err != nil {
return nil, err
}
fobj.Name = (*int8)(unsafe.Pointer(&bs[0]))
s := stat.Sys().(*syscall.Stat_t)
fobj.Atim.Sec = s.Atim.Sec
fobj.Atim.Nsec = s.Atim.Nsec
fobj.Mtim.Sec = s.Mtim.Sec
fobj.Mtim.Nsec = s.Mtim.Nsec
fobj.Ctim.Sec = s.Ctim.Sec
fobj.Ctim.Nsec = s.Ctim.Nsec
return fobj, nil
}
// GetOne wraps port_get(3c) and returns a single PortEvent.
func (e *EventPort) GetOne(t *Timespec) (*PortEvent, error) {
pe := new(portEvent)
_, err := port_get(e.port, pe, t)
if err != nil {
return nil, err
}
p := new(PortEvent)
p.Events = pe.Events
p.Source = pe.Source
e.mu.Lock()
defer e.mu.Unlock()
switch pe.Source {
case PORT_SOURCE_FD:
p.Fd = uintptr(pe.Object)
cookie := (*interface{})(unsafe.Pointer(pe.User))
p.Cookie = *cookie
delete(e.fds, p.Fd)
case PORT_SOURCE_FILE:
p.fobj = (*fileObj)(unsafe.Pointer(uintptr(pe.Object)))
p.Path = BytePtrToString((*byte)(unsafe.Pointer(p.fobj.Name)))
cookie := (*interface{})(unsafe.Pointer(pe.User))
p.Cookie = *cookie
delete(e.paths, p.Path)
}
return p, nil
}
// Pending wraps port_getn(3c) and returns how many events are pending.
func (e *EventPort) Pending() (int, error) {
var n uint32 = 0
_, err := port_getn(e.port, nil, 0, &n, nil)
return int(n), err
}
// Get wraps port_getn(3c) and fills a slice of PortEvent.
// It will block until either min events have been received
// or the timeout has been exceeded. It will return how many
// events were actually received along with any error information.
func (e *EventPort) Get(s []PortEvent, min int, timeout *Timespec) (int, error) {
if min == 0 {
return 0, fmt.Errorf("need to request at least one event or use Pending() instead")
}
if len(s) < min {
return 0, fmt.Errorf("len(s) (%d) is less than min events requested (%d)", len(s), min)
}
got := uint32(min)
max := uint32(len(s))
var err error
ps := make([]portEvent, max, max)
_, err = port_getn(e.port, &ps[0], max, &got, timeout)
// got will be trustworthy with ETIME, but not any other error.
if err != nil && err != ETIME {
return 0, err
}
e.mu.Lock()
defer e.mu.Unlock()
for i := 0; i < int(got); i++ {
s[i].Events = ps[i].Events
s[i].Source = ps[i].Source
switch ps[i].Source {
case PORT_SOURCE_FD:
s[i].Fd = uintptr(ps[i].Object)
cookie := (*interface{})(unsafe.Pointer(ps[i].User))
s[i].Cookie = *cookie
delete(e.fds, s[i].Fd)
case PORT_SOURCE_FILE:
s[i].fobj = (*fileObj)(unsafe.Pointer(uintptr(ps[i].Object)))
s[i].Path = BytePtrToString((*byte)(unsafe.Pointer(s[i].fobj.Name)))
cookie := (*interface{})(unsafe.Pointer(ps[i].User))
s[i].Cookie = *cookie
delete(e.paths, s[i].Path)
}
}
return int(got), err
}

View file

@ -313,6 +313,10 @@ func Recvfrom(fd int, p []byte, flags int) (n int, from Sockaddr, err error) {
return return
} }
func Send(s int, buf []byte, flags int) (err error) {
return sendto(s, buf, flags, nil, 0)
}
func Sendto(fd int, p []byte, flags int, to Sockaddr) (err error) { func Sendto(fd int, p []byte, flags int, to Sockaddr) (err error) {
ptr, n, err := to.sockaddr() ptr, n, err := to.sockaddr()
if err != nil { if err != nil {

View file

@ -1206,6 +1206,7 @@ const (
RTF_DONE = 0x40 RTF_DONE = 0x40
RTF_DYNAMIC = 0x10 RTF_DYNAMIC = 0x10
RTF_GATEWAY = 0x2 RTF_GATEWAY = 0x2
RTF_GLOBAL = 0x40000000
RTF_HOST = 0x4 RTF_HOST = 0x4
RTF_IFREF = 0x4000000 RTF_IFREF = 0x4000000
RTF_IFSCOPE = 0x1000000 RTF_IFSCOPE = 0x1000000

View file

@ -1206,6 +1206,7 @@ const (
RTF_DONE = 0x40 RTF_DONE = 0x40
RTF_DYNAMIC = 0x10 RTF_DYNAMIC = 0x10
RTF_GATEWAY = 0x2 RTF_GATEWAY = 0x2
RTF_GLOBAL = 0x40000000
RTF_HOST = 0x4 RTF_HOST = 0x4
RTF_IFREF = 0x4000000 RTF_IFREF = 0x4000000
RTF_IFSCOPE = 0x1000000 RTF_IFSCOPE = 0x1000000

View file

@ -228,6 +228,8 @@ const (
BPF_OR = 0x40 BPF_OR = 0x40
BPF_PSEUDO_BTF_ID = 0x3 BPF_PSEUDO_BTF_ID = 0x3
BPF_PSEUDO_CALL = 0x1 BPF_PSEUDO_CALL = 0x1
BPF_PSEUDO_FUNC = 0x4
BPF_PSEUDO_KFUNC_CALL = 0x2
BPF_PSEUDO_MAP_FD = 0x1 BPF_PSEUDO_MAP_FD = 0x1
BPF_PSEUDO_MAP_VALUE = 0x2 BPF_PSEUDO_MAP_VALUE = 0x2
BPF_RET = 0x6 BPF_RET = 0x6
@ -475,6 +477,8 @@ const (
DM_LIST_VERSIONS = 0xc138fd0d DM_LIST_VERSIONS = 0xc138fd0d
DM_MAX_TYPE_NAME = 0x10 DM_MAX_TYPE_NAME = 0x10
DM_NAME_LEN = 0x80 DM_NAME_LEN = 0x80
DM_NAME_LIST_FLAG_DOESNT_HAVE_UUID = 0x2
DM_NAME_LIST_FLAG_HAS_UUID = 0x1
DM_NOFLUSH_FLAG = 0x800 DM_NOFLUSH_FLAG = 0x800
DM_PERSISTENT_DEV_FLAG = 0x8 DM_PERSISTENT_DEV_FLAG = 0x8
DM_QUERY_INACTIVE_TABLE_FLAG = 0x1000 DM_QUERY_INACTIVE_TABLE_FLAG = 0x1000
@ -494,9 +498,9 @@ const (
DM_UUID_FLAG = 0x4000 DM_UUID_FLAG = 0x4000
DM_UUID_LEN = 0x81 DM_UUID_LEN = 0x81
DM_VERSION = 0xc138fd00 DM_VERSION = 0xc138fd00
DM_VERSION_EXTRA = "-ioctl (2021-02-01)" DM_VERSION_EXTRA = "-ioctl (2021-03-22)"
DM_VERSION_MAJOR = 0x4 DM_VERSION_MAJOR = 0x4
DM_VERSION_MINOR = 0x2c DM_VERSION_MINOR = 0x2d
DM_VERSION_PATCHLEVEL = 0x0 DM_VERSION_PATCHLEVEL = 0x0
DT_BLK = 0x6 DT_BLK = 0x6
DT_CHR = 0x2 DT_CHR = 0x2
@ -981,12 +985,6 @@ const (
HPFS_SUPER_MAGIC = 0xf995e849 HPFS_SUPER_MAGIC = 0xf995e849
HUGETLBFS_MAGIC = 0x958458f6 HUGETLBFS_MAGIC = 0x958458f6
IBSHIFT = 0x10 IBSHIFT = 0x10
ICMPV6_FILTER = 0x1
ICMPV6_FILTER_BLOCK = 0x1
ICMPV6_FILTER_BLOCKOTHERS = 0x3
ICMPV6_FILTER_PASS = 0x2
ICMPV6_FILTER_PASSONLY = 0x4
ICMP_FILTER = 0x1
ICRNL = 0x100 ICRNL = 0x100
IFA_F_DADFAILED = 0x8 IFA_F_DADFAILED = 0x8
IFA_F_DEPRECATED = 0x20 IFA_F_DEPRECATED = 0x20
@ -1257,6 +1255,7 @@ const (
KEXEC_ARCH_PARISC = 0xf0000 KEXEC_ARCH_PARISC = 0xf0000
KEXEC_ARCH_PPC = 0x140000 KEXEC_ARCH_PPC = 0x140000
KEXEC_ARCH_PPC64 = 0x150000 KEXEC_ARCH_PPC64 = 0x150000
KEXEC_ARCH_RISCV = 0xf30000
KEXEC_ARCH_S390 = 0x160000 KEXEC_ARCH_S390 = 0x160000
KEXEC_ARCH_SH = 0x2a0000 KEXEC_ARCH_SH = 0x2a0000
KEXEC_ARCH_X86_64 = 0x3e0000 KEXEC_ARCH_X86_64 = 0x3e0000
@ -1756,14 +1755,19 @@ const (
PERF_ATTR_SIZE_VER4 = 0x68 PERF_ATTR_SIZE_VER4 = 0x68
PERF_ATTR_SIZE_VER5 = 0x70 PERF_ATTR_SIZE_VER5 = 0x70
PERF_ATTR_SIZE_VER6 = 0x78 PERF_ATTR_SIZE_VER6 = 0x78
PERF_ATTR_SIZE_VER7 = 0x80
PERF_AUX_FLAG_COLLISION = 0x8 PERF_AUX_FLAG_COLLISION = 0x8
PERF_AUX_FLAG_CORESIGHT_FORMAT_CORESIGHT = 0x0
PERF_AUX_FLAG_CORESIGHT_FORMAT_RAW = 0x100
PERF_AUX_FLAG_OVERWRITE = 0x2 PERF_AUX_FLAG_OVERWRITE = 0x2
PERF_AUX_FLAG_PARTIAL = 0x4 PERF_AUX_FLAG_PARTIAL = 0x4
PERF_AUX_FLAG_PMU_FORMAT_TYPE_MASK = 0xff00
PERF_AUX_FLAG_TRUNCATED = 0x1 PERF_AUX_FLAG_TRUNCATED = 0x1
PERF_FLAG_FD_CLOEXEC = 0x8 PERF_FLAG_FD_CLOEXEC = 0x8
PERF_FLAG_FD_NO_GROUP = 0x1 PERF_FLAG_FD_NO_GROUP = 0x1
PERF_FLAG_FD_OUTPUT = 0x2 PERF_FLAG_FD_OUTPUT = 0x2
PERF_FLAG_PID_CGROUP = 0x4 PERF_FLAG_PID_CGROUP = 0x4
PERF_HW_EVENT_MASK = 0xffffffff
PERF_MAX_CONTEXTS_PER_STACK = 0x8 PERF_MAX_CONTEXTS_PER_STACK = 0x8
PERF_MAX_STACK_DEPTH = 0x7f PERF_MAX_STACK_DEPTH = 0x7f
PERF_MEM_BLK_ADDR = 0x4 PERF_MEM_BLK_ADDR = 0x4
@ -1822,6 +1826,7 @@ const (
PERF_MEM_TLB_OS = 0x40 PERF_MEM_TLB_OS = 0x40
PERF_MEM_TLB_SHIFT = 0x1a PERF_MEM_TLB_SHIFT = 0x1a
PERF_MEM_TLB_WK = 0x20 PERF_MEM_TLB_WK = 0x20
PERF_PMU_TYPE_SHIFT = 0x20
PERF_RECORD_KSYMBOL_FLAGS_UNREGISTER = 0x1 PERF_RECORD_KSYMBOL_FLAGS_UNREGISTER = 0x1
PERF_RECORD_MISC_COMM_EXEC = 0x2000 PERF_RECORD_MISC_COMM_EXEC = 0x2000
PERF_RECORD_MISC_CPUMODE_MASK = 0x7 PERF_RECORD_MISC_CPUMODE_MASK = 0x7
@ -1921,7 +1926,9 @@ const (
PR_PAC_APGAKEY = 0x10 PR_PAC_APGAKEY = 0x10
PR_PAC_APIAKEY = 0x1 PR_PAC_APIAKEY = 0x1
PR_PAC_APIBKEY = 0x2 PR_PAC_APIBKEY = 0x2
PR_PAC_GET_ENABLED_KEYS = 0x3d
PR_PAC_RESET_KEYS = 0x36 PR_PAC_RESET_KEYS = 0x36
PR_PAC_SET_ENABLED_KEYS = 0x3c
PR_SET_CHILD_SUBREAPER = 0x24 PR_SET_CHILD_SUBREAPER = 0x24
PR_SET_DUMPABLE = 0x4 PR_SET_DUMPABLE = 0x4
PR_SET_ENDIAN = 0x14 PR_SET_ENDIAN = 0x14
@ -2003,6 +2010,7 @@ const (
PTRACE_GETREGSET = 0x4204 PTRACE_GETREGSET = 0x4204
PTRACE_GETSIGINFO = 0x4202 PTRACE_GETSIGINFO = 0x4202
PTRACE_GETSIGMASK = 0x420a PTRACE_GETSIGMASK = 0x420a
PTRACE_GET_RSEQ_CONFIGURATION = 0x420f
PTRACE_GET_SYSCALL_INFO = 0x420e PTRACE_GET_SYSCALL_INFO = 0x420e
PTRACE_INTERRUPT = 0x4207 PTRACE_INTERRUPT = 0x4207
PTRACE_KILL = 0x8 PTRACE_KILL = 0x8
@ -2163,6 +2171,7 @@ const (
RTM_DELNEIGH = 0x1d RTM_DELNEIGH = 0x1d
RTM_DELNETCONF = 0x51 RTM_DELNETCONF = 0x51
RTM_DELNEXTHOP = 0x69 RTM_DELNEXTHOP = 0x69
RTM_DELNEXTHOPBUCKET = 0x75
RTM_DELNSID = 0x59 RTM_DELNSID = 0x59
RTM_DELQDISC = 0x25 RTM_DELQDISC = 0x25
RTM_DELROUTE = 0x19 RTM_DELROUTE = 0x19
@ -2193,6 +2202,7 @@ const (
RTM_GETNEIGHTBL = 0x42 RTM_GETNEIGHTBL = 0x42
RTM_GETNETCONF = 0x52 RTM_GETNETCONF = 0x52
RTM_GETNEXTHOP = 0x6a RTM_GETNEXTHOP = 0x6a
RTM_GETNEXTHOPBUCKET = 0x76
RTM_GETNSID = 0x5a RTM_GETNSID = 0x5a
RTM_GETQDISC = 0x26 RTM_GETQDISC = 0x26
RTM_GETROUTE = 0x1a RTM_GETROUTE = 0x1a
@ -2201,7 +2211,7 @@ const (
RTM_GETTCLASS = 0x2a RTM_GETTCLASS = 0x2a
RTM_GETTFILTER = 0x2e RTM_GETTFILTER = 0x2e
RTM_GETVLAN = 0x72 RTM_GETVLAN = 0x72
RTM_MAX = 0x73 RTM_MAX = 0x77
RTM_NEWACTION = 0x30 RTM_NEWACTION = 0x30
RTM_NEWADDR = 0x14 RTM_NEWADDR = 0x14
RTM_NEWADDRLABEL = 0x48 RTM_NEWADDRLABEL = 0x48
@ -2215,6 +2225,7 @@ const (
RTM_NEWNEIGHTBL = 0x40 RTM_NEWNEIGHTBL = 0x40
RTM_NEWNETCONF = 0x50 RTM_NEWNETCONF = 0x50
RTM_NEWNEXTHOP = 0x68 RTM_NEWNEXTHOP = 0x68
RTM_NEWNEXTHOPBUCKET = 0x74
RTM_NEWNSID = 0x58 RTM_NEWNSID = 0x58
RTM_NEWNVLAN = 0x70 RTM_NEWNVLAN = 0x70
RTM_NEWPREFIX = 0x34 RTM_NEWPREFIX = 0x34
@ -2224,8 +2235,8 @@ const (
RTM_NEWSTATS = 0x5c RTM_NEWSTATS = 0x5c
RTM_NEWTCLASS = 0x28 RTM_NEWTCLASS = 0x28
RTM_NEWTFILTER = 0x2c RTM_NEWTFILTER = 0x2c
RTM_NR_FAMILIES = 0x19 RTM_NR_FAMILIES = 0x1a
RTM_NR_MSGTYPES = 0x64 RTM_NR_MSGTYPES = 0x68
RTM_SETDCB = 0x4f RTM_SETDCB = 0x4f
RTM_SETLINK = 0x13 RTM_SETLINK = 0x13
RTM_SETNEIGHTBL = 0x43 RTM_SETNEIGHTBL = 0x43
@ -2253,6 +2264,7 @@ const (
RTPROT_MROUTED = 0x11 RTPROT_MROUTED = 0x11
RTPROT_MRT = 0xa RTPROT_MRT = 0xa
RTPROT_NTK = 0xf RTPROT_NTK = 0xf
RTPROT_OPENR = 0x63
RTPROT_OSPF = 0xbc RTPROT_OSPF = 0xbc
RTPROT_RA = 0x9 RTPROT_RA = 0x9
RTPROT_REDIRECT = 0x1 RTPROT_REDIRECT = 0x1
@ -2536,6 +2548,14 @@ const (
TCOFLUSH = 0x1 TCOFLUSH = 0x1
TCOOFF = 0x0 TCOOFF = 0x0
TCOON = 0x1 TCOON = 0x1
TCPOPT_EOL = 0x0
TCPOPT_MAXSEG = 0x2
TCPOPT_NOP = 0x1
TCPOPT_SACK = 0x5
TCPOPT_SACK_PERMITTED = 0x4
TCPOPT_TIMESTAMP = 0x8
TCPOPT_TSTAMP_HDR = 0x101080a
TCPOPT_WINDOW = 0x3
TCP_CC_INFO = 0x1a TCP_CC_INFO = 0x1a
TCP_CM_INQ = 0x24 TCP_CM_INQ = 0x24
TCP_CONGESTION = 0xd TCP_CONGESTION = 0xd

View file

@ -147,6 +147,7 @@ const (
NS_GET_USERNS = 0xb701 NS_GET_USERNS = 0xb701
OLCUC = 0x2 OLCUC = 0x2
ONLCR = 0x4 ONLCR = 0x4
OTPERASE = 0x400c4d19
OTPGETREGIONCOUNT = 0x40044d0e OTPGETREGIONCOUNT = 0x40044d0e
OTPGETREGIONINFO = 0x400c4d0f OTPGETREGIONINFO = 0x400c4d0f
OTPLOCK = 0x800c4d10 OTPLOCK = 0x800c4d10

View file

@ -147,6 +147,7 @@ const (
NS_GET_USERNS = 0xb701 NS_GET_USERNS = 0xb701
OLCUC = 0x2 OLCUC = 0x2
ONLCR = 0x4 ONLCR = 0x4
OTPERASE = 0x400c4d19
OTPGETREGIONCOUNT = 0x40044d0e OTPGETREGIONCOUNT = 0x40044d0e
OTPGETREGIONINFO = 0x400c4d0f OTPGETREGIONINFO = 0x400c4d0f
OTPLOCK = 0x800c4d10 OTPLOCK = 0x800c4d10

View file

@ -145,6 +145,7 @@ const (
NS_GET_USERNS = 0xb701 NS_GET_USERNS = 0xb701
OLCUC = 0x2 OLCUC = 0x2
ONLCR = 0x4 ONLCR = 0x4
OTPERASE = 0x400c4d19
OTPGETREGIONCOUNT = 0x40044d0e OTPGETREGIONCOUNT = 0x40044d0e
OTPGETREGIONINFO = 0x400c4d0f OTPGETREGIONINFO = 0x400c4d0f
OTPLOCK = 0x800c4d10 OTPLOCK = 0x800c4d10

View file

@ -148,6 +148,7 @@ const (
NS_GET_USERNS = 0xb701 NS_GET_USERNS = 0xb701
OLCUC = 0x2 OLCUC = 0x2
ONLCR = 0x4 ONLCR = 0x4
OTPERASE = 0x400c4d19
OTPGETREGIONCOUNT = 0x40044d0e OTPGETREGIONCOUNT = 0x40044d0e
OTPGETREGIONINFO = 0x400c4d0f OTPGETREGIONINFO = 0x400c4d0f
OTPLOCK = 0x800c4d10 OTPLOCK = 0x800c4d10

View file

@ -145,6 +145,7 @@ const (
NS_GET_USERNS = 0x2000b701 NS_GET_USERNS = 0x2000b701
OLCUC = 0x2 OLCUC = 0x2
ONLCR = 0x4 ONLCR = 0x4
OTPERASE = 0x800c4d19
OTPGETREGIONCOUNT = 0x80044d0e OTPGETREGIONCOUNT = 0x80044d0e
OTPGETREGIONINFO = 0x800c4d0f OTPGETREGIONINFO = 0x800c4d0f
OTPLOCK = 0x400c4d10 OTPLOCK = 0x400c4d10

View file

@ -145,6 +145,7 @@ const (
NS_GET_USERNS = 0x2000b701 NS_GET_USERNS = 0x2000b701
OLCUC = 0x2 OLCUC = 0x2
ONLCR = 0x4 ONLCR = 0x4
OTPERASE = 0x800c4d19
OTPGETREGIONCOUNT = 0x80044d0e OTPGETREGIONCOUNT = 0x80044d0e
OTPGETREGIONINFO = 0x800c4d0f OTPGETREGIONINFO = 0x800c4d0f
OTPLOCK = 0x400c4d10 OTPLOCK = 0x400c4d10

View file

@ -145,6 +145,7 @@ const (
NS_GET_USERNS = 0x2000b701 NS_GET_USERNS = 0x2000b701
OLCUC = 0x2 OLCUC = 0x2
ONLCR = 0x4 ONLCR = 0x4
OTPERASE = 0x800c4d19
OTPGETREGIONCOUNT = 0x80044d0e OTPGETREGIONCOUNT = 0x80044d0e
OTPGETREGIONINFO = 0x800c4d0f OTPGETREGIONINFO = 0x800c4d0f
OTPLOCK = 0x400c4d10 OTPLOCK = 0x400c4d10

View file

@ -145,6 +145,7 @@ const (
NS_GET_USERNS = 0x2000b701 NS_GET_USERNS = 0x2000b701
OLCUC = 0x2 OLCUC = 0x2
ONLCR = 0x4 ONLCR = 0x4
OTPERASE = 0x800c4d19
OTPGETREGIONCOUNT = 0x80044d0e OTPGETREGIONCOUNT = 0x80044d0e
OTPGETREGIONINFO = 0x800c4d0f OTPGETREGIONINFO = 0x800c4d0f
OTPLOCK = 0x400c4d10 OTPLOCK = 0x400c4d10

View file

@ -147,6 +147,7 @@ const (
NS_GET_USERNS = 0x2000b701 NS_GET_USERNS = 0x2000b701
OLCUC = 0x4 OLCUC = 0x4
ONLCR = 0x2 ONLCR = 0x2
OTPERASE = 0x800c4d19
OTPGETREGIONCOUNT = 0x80044d0e OTPGETREGIONCOUNT = 0x80044d0e
OTPGETREGIONINFO = 0x800c4d0f OTPGETREGIONINFO = 0x800c4d0f
OTPLOCK = 0x400c4d10 OTPLOCK = 0x400c4d10

View file

@ -147,6 +147,7 @@ const (
NS_GET_USERNS = 0x2000b701 NS_GET_USERNS = 0x2000b701
OLCUC = 0x4 OLCUC = 0x4
ONLCR = 0x2 ONLCR = 0x2
OTPERASE = 0x800c4d19
OTPGETREGIONCOUNT = 0x80044d0e OTPGETREGIONCOUNT = 0x80044d0e
OTPGETREGIONINFO = 0x800c4d0f OTPGETREGIONINFO = 0x800c4d0f
OTPLOCK = 0x400c4d10 OTPLOCK = 0x400c4d10

View file

@ -147,6 +147,7 @@ const (
NS_GET_USERNS = 0x2000b701 NS_GET_USERNS = 0x2000b701
OLCUC = 0x4 OLCUC = 0x4
ONLCR = 0x2 ONLCR = 0x2
OTPERASE = 0x800c4d19
OTPGETREGIONCOUNT = 0x80044d0e OTPGETREGIONCOUNT = 0x80044d0e
OTPGETREGIONINFO = 0x800c4d0f OTPGETREGIONINFO = 0x800c4d0f
OTPLOCK = 0x400c4d10 OTPLOCK = 0x400c4d10

View file

@ -145,6 +145,7 @@ const (
NS_GET_USERNS = 0xb701 NS_GET_USERNS = 0xb701
OLCUC = 0x2 OLCUC = 0x2
ONLCR = 0x4 ONLCR = 0x4
OTPERASE = 0x400c4d19
OTPGETREGIONCOUNT = 0x40044d0e OTPGETREGIONCOUNT = 0x40044d0e
OTPGETREGIONINFO = 0x400c4d0f OTPGETREGIONINFO = 0x400c4d0f
OTPLOCK = 0x800c4d10 OTPLOCK = 0x800c4d10

View file

@ -145,6 +145,7 @@ const (
NS_GET_USERNS = 0xb701 NS_GET_USERNS = 0xb701
OLCUC = 0x2 OLCUC = 0x2
ONLCR = 0x4 ONLCR = 0x4
OTPERASE = 0x400c4d19
OTPGETREGIONCOUNT = 0x40044d0e OTPGETREGIONCOUNT = 0x40044d0e
OTPGETREGIONINFO = 0x400c4d0f OTPGETREGIONINFO = 0x400c4d0f
OTPLOCK = 0x800c4d10 OTPLOCK = 0x800c4d10

View file

@ -150,6 +150,7 @@ const (
NS_GET_USERNS = 0x2000b701 NS_GET_USERNS = 0x2000b701
OLCUC = 0x2 OLCUC = 0x2
ONLCR = 0x4 ONLCR = 0x4
OTPERASE = 0x800c4d19
OTPGETREGIONCOUNT = 0x80044d0e OTPGETREGIONCOUNT = 0x80044d0e
OTPGETREGIONINFO = 0x800c4d0f OTPGETREGIONINFO = 0x800c4d0f
OTPLOCK = 0x400c4d10 OTPLOCK = 0x400c4d10

View file

@ -1020,7 +1020,10 @@ const (
RLIMIT_CPU = 0x0 RLIMIT_CPU = 0x0
RLIMIT_DATA = 0x2 RLIMIT_DATA = 0x2
RLIMIT_FSIZE = 0x1 RLIMIT_FSIZE = 0x1
RLIMIT_MEMLOCK = 0x6
RLIMIT_NOFILE = 0x8 RLIMIT_NOFILE = 0x8
RLIMIT_NPROC = 0x7
RLIMIT_RSS = 0x5
RLIMIT_STACK = 0x3 RLIMIT_STACK = 0x3
RLIM_INFINITY = 0x7fffffffffffffff RLIM_INFINITY = 0x7fffffffffffffff
RTAX_AUTHOR = 0x6 RTAX_AUTHOR = 0x6

View file

@ -1020,7 +1020,10 @@ const (
RLIMIT_CPU = 0x0 RLIMIT_CPU = 0x0
RLIMIT_DATA = 0x2 RLIMIT_DATA = 0x2
RLIMIT_FSIZE = 0x1 RLIMIT_FSIZE = 0x1
RLIMIT_MEMLOCK = 0x6
RLIMIT_NOFILE = 0x8 RLIMIT_NOFILE = 0x8
RLIMIT_NPROC = 0x7
RLIMIT_RSS = 0x5
RLIMIT_STACK = 0x3 RLIMIT_STACK = 0x3
RLIM_INFINITY = 0x7fffffffffffffff RLIM_INFINITY = 0x7fffffffffffffff
RTAX_AUTHOR = 0x6 RTAX_AUTHOR = 0x6

View file

@ -48,6 +48,16 @@ func ioctl(fd int, req uint, arg uintptr) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) {
_, _, e1 := Syscall(SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(arg))
if e1 != 0 {
err = errnoErr(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Linkat(olddirfd int, oldpath string, newdirfd int, newpath string, flags int) (err error) { func Linkat(olddirfd int, oldpath string, newdirfd int, newpath string, flags int) (err error) {
var _p0 *byte var _p0 *byte
_p0, err = BytePtrFromString(oldpath) _p0, err = BytePtrFromString(oldpath)
@ -1201,7 +1211,7 @@ func PivotRoot(newroot string, putold string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) { func Prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0) _, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
if e1 != 0 { if e1 != 0 {
err = errnoErr(e1) err = errnoErr(e1)

View file

@ -141,6 +141,11 @@ import (
//go:cgo_import_dynamic libc_getpeername getpeername "libsocket.so" //go:cgo_import_dynamic libc_getpeername getpeername "libsocket.so"
//go:cgo_import_dynamic libc_setsockopt setsockopt "libsocket.so" //go:cgo_import_dynamic libc_setsockopt setsockopt "libsocket.so"
//go:cgo_import_dynamic libc_recvfrom recvfrom "libsocket.so" //go:cgo_import_dynamic libc_recvfrom recvfrom "libsocket.so"
//go:cgo_import_dynamic libc_port_create port_create "libc.so"
//go:cgo_import_dynamic libc_port_associate port_associate "libc.so"
//go:cgo_import_dynamic libc_port_dissociate port_dissociate "libc.so"
//go:cgo_import_dynamic libc_port_get port_get "libc.so"
//go:cgo_import_dynamic libc_port_getn port_getn "libc.so"
//go:linkname procpipe libc_pipe //go:linkname procpipe libc_pipe
//go:linkname procpipe2 libc_pipe2 //go:linkname procpipe2 libc_pipe2
@ -272,6 +277,11 @@ import (
//go:linkname procgetpeername libc_getpeername //go:linkname procgetpeername libc_getpeername
//go:linkname procsetsockopt libc_setsockopt //go:linkname procsetsockopt libc_setsockopt
//go:linkname procrecvfrom libc_recvfrom //go:linkname procrecvfrom libc_recvfrom
//go:linkname procport_create libc_port_create
//go:linkname procport_associate libc_port_associate
//go:linkname procport_dissociate libc_port_dissociate
//go:linkname procport_get libc_port_get
//go:linkname procport_getn libc_port_getn
var ( var (
procpipe, procpipe,
@ -403,7 +413,12 @@ var (
proc__xnet_getsockopt, proc__xnet_getsockopt,
procgetpeername, procgetpeername,
procsetsockopt, procsetsockopt,
procrecvfrom syscallFunc procrecvfrom,
procport_create,
procport_associate,
procport_dissociate,
procport_get,
procport_getn syscallFunc
) )
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@ -1981,3 +1996,58 @@ func recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Sockl
} }
return return
} }
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func port_create() (n int, err error) {
r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&procport_create)), 0, 0, 0, 0, 0, 0, 0)
n = int(r0)
if e1 != 0 {
err = e1
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func port_associate(port int, source int, object uintptr, events int, user *byte) (n int, err error) {
r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&procport_associate)), 5, uintptr(port), uintptr(source), uintptr(object), uintptr(events), uintptr(unsafe.Pointer(user)), 0)
n = int(r0)
if e1 != 0 {
err = e1
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func port_dissociate(port int, source int, object uintptr) (n int, err error) {
r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&procport_dissociate)), 3, uintptr(port), uintptr(source), uintptr(object), 0, 0, 0)
n = int(r0)
if e1 != 0 {
err = e1
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func port_get(port int, pe *portEvent, timeout *Timespec) (n int, err error) {
r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&procport_get)), 3, uintptr(port), uintptr(unsafe.Pointer(pe)), uintptr(unsafe.Pointer(timeout)), 0, 0, 0)
n = int(r0)
if e1 != 0 {
err = e1
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func port_getn(port int, pe *portEvent, max uint32, nget *uint32, timeout *Timespec) (n int, err error) {
r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&procport_getn)), 5, uintptr(port), uintptr(unsafe.Pointer(pe)), uintptr(max), uintptr(unsafe.Pointer(nget)), uintptr(unsafe.Pointer(timeout)), 0)
n = int(r0)
if e1 != 0 {
err = e1
}
return
}

View file

@ -439,4 +439,7 @@ const (
SYS_PROCESS_MADVISE = 440 SYS_PROCESS_MADVISE = 440
SYS_EPOLL_PWAIT2 = 441 SYS_EPOLL_PWAIT2 = 441
SYS_MOUNT_SETATTR = 442 SYS_MOUNT_SETATTR = 442
SYS_LANDLOCK_CREATE_RULESET = 444
SYS_LANDLOCK_ADD_RULE = 445
SYS_LANDLOCK_RESTRICT_SELF = 446
) )

View file

@ -361,4 +361,7 @@ const (
SYS_PROCESS_MADVISE = 440 SYS_PROCESS_MADVISE = 440
SYS_EPOLL_PWAIT2 = 441 SYS_EPOLL_PWAIT2 = 441
SYS_MOUNT_SETATTR = 442 SYS_MOUNT_SETATTR = 442
SYS_LANDLOCK_CREATE_RULESET = 444
SYS_LANDLOCK_ADD_RULE = 445
SYS_LANDLOCK_RESTRICT_SELF = 446
) )

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