From ce71a5a7902963538fc54583588850563f6746cc Mon Sep 17 00:00:00 2001 From: kim <89579420+NyaaaWhatsUpDoc@users.noreply.github.com> Date: Tue, 31 Oct 2023 11:12:22 +0000 Subject: [PATCH] [feature] add per-uri dereferencer locks (#2291) --- go.mod | 13 +- go.sum | 28 +- ...20220612091800_duplicated_media_cleanup.go | 9 +- internal/federation/dereferencing/account.go | 139 +- .../federation/dereferencing/dereferencer.go | 23 +- internal/federation/dereferencing/emoji.go | 17 +- internal/federation/dereferencing/status.go | 171 +- .../dereferencing/{error.go => util.go} | 11 + internal/federation/federatingdb/db.go | 3 - internal/federation/federatingdb/lock.go | 7 +- internal/gtsmodel/status.go | 123 +- internal/state/state.go | 6 + vendor/codeberg.org/gruf/go-fastpath/LICENSE | 9 - .../codeberg.org/gruf/go-fastpath/README.md | 1 - .../gruf/go-fastpath/benchmarks.png | Bin 108279 -> 0 bytes vendor/codeberg.org/gruf/go-fastpath/path.go | 319 ---- vendor/codeberg.org/gruf/go-hashenc/LICENSE | 9 - vendor/codeberg.org/gruf/go-hashenc/README.md | 1 - vendor/codeberg.org/gruf/go-hashenc/enc.go | 42 - .../codeberg.org/gruf/go-hashenc/hashenc.go | 58 - vendor/codeberg.org/gruf/go-iotools/read.go | 8 + vendor/codeberg.org/gruf/go-iotools/write.go | 8 + vendor/codeberg.org/gruf/go-mutexes/LICENSE | 2 +- vendor/codeberg.org/gruf/go-mutexes/map.go | 632 +++---- .../codeberg.org/gruf/go-mutexes/map_pool.go | 39 - vendor/codeberg.org/gruf/go-pools/LICENSE | 9 - vendor/codeberg.org/gruf/go-pools/README.md | 2 - vendor/codeberg.org/gruf/go-pools/bufio.go | 89 - vendor/codeberg.org/gruf/go-pools/bytes.go | 46 - vendor/codeberg.org/gruf/go-pools/fastpath.go | 46 - vendor/codeberg.org/gruf/go-pools/henc.go | 46 - vendor/codeberg.org/gruf/go-pools/pool.go | 387 ---- .../gruf/go-store/v2/kv/iterator.go | 63 - .../codeberg.org/gruf/go-store/v2/kv/state.go | 116 -- .../codeberg.org/gruf/go-store/v2/kv/store.go | 267 --- .../v2/storage/{block.go => block.archived} | 0 .../go-store/v2/storage/block_test.archived | 38 + .../klauspost/compress/flate/deflate.go | 29 + .../klauspost/compress/flate/fast_encoder.go | 23 - .../klauspost/compress/flate/inflate.go | 66 +- .../klauspost/compress/flate/inflate_gen.go | 34 +- .../klauspost/compress/flate/level5.go | 398 ++++ .../compress/flate/matchlen_amd64.go | 16 + .../klauspost/compress/flate/matchlen_amd64.s | 68 + .../compress/flate/matchlen_generic.go | 33 + .../klauspost/compress/gzip/gunzip.go | 1 + .../klauspost/compress/gzip/gzip.go | 21 + .../github.com/klauspost/compress/s2/dict.go | 19 + .../klauspost/compress/s2/encode.go | 2 +- .../klauspost/compress/s2/encode_best.go | 3 + .../klauspost/compress/s2/encode_go.go | 2 + .../klauspost/compress/s2/encodeblock_amd64.s | 1610 ++++++++++++----- .../github.com/klauspost/compress/s2/index.go | 20 +- vendor/modules.txt | 19 +- 54 files changed, 2432 insertions(+), 2719 deletions(-) rename internal/federation/dereferencing/{error.go => util.go} (83%) delete mode 100644 vendor/codeberg.org/gruf/go-fastpath/LICENSE delete mode 100644 vendor/codeberg.org/gruf/go-fastpath/README.md delete mode 100644 vendor/codeberg.org/gruf/go-fastpath/benchmarks.png delete mode 100644 vendor/codeberg.org/gruf/go-fastpath/path.go delete mode 100644 vendor/codeberg.org/gruf/go-hashenc/LICENSE delete mode 100644 vendor/codeberg.org/gruf/go-hashenc/README.md delete mode 100644 vendor/codeberg.org/gruf/go-hashenc/enc.go delete mode 100644 vendor/codeberg.org/gruf/go-hashenc/hashenc.go delete mode 100644 vendor/codeberg.org/gruf/go-mutexes/map_pool.go delete mode 100644 vendor/codeberg.org/gruf/go-pools/LICENSE delete mode 100644 vendor/codeberg.org/gruf/go-pools/README.md delete mode 100644 vendor/codeberg.org/gruf/go-pools/bufio.go delete mode 100644 vendor/codeberg.org/gruf/go-pools/bytes.go delete mode 100644 vendor/codeberg.org/gruf/go-pools/fastpath.go delete mode 100644 vendor/codeberg.org/gruf/go-pools/henc.go delete mode 100644 vendor/codeberg.org/gruf/go-pools/pool.go delete mode 100644 vendor/codeberg.org/gruf/go-store/v2/kv/iterator.go delete mode 100644 vendor/codeberg.org/gruf/go-store/v2/kv/state.go delete mode 100644 vendor/codeberg.org/gruf/go-store/v2/kv/store.go rename vendor/codeberg.org/gruf/go-store/v2/storage/{block.go => block.archived} (100%) create mode 100644 vendor/codeberg.org/gruf/go-store/v2/storage/block_test.archived create mode 100644 vendor/github.com/klauspost/compress/flate/matchlen_amd64.go create mode 100644 vendor/github.com/klauspost/compress/flate/matchlen_amd64.s create mode 100644 vendor/github.com/klauspost/compress/flate/matchlen_generic.go diff --git a/go.mod b/go.mod index abd228e85..16d70059a 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,8 @@ go 1.21 toolchain go1.21.3 +replace codeberg.org/gruf/go-mutexes => ../go-mutexes + require ( codeberg.org/gruf/go-bytesize v1.0.2 codeberg.org/gruf/go-byteutil v1.1.2 @@ -11,13 +13,13 @@ require ( codeberg.org/gruf/go-debug v1.3.0 codeberg.org/gruf/go-errors/v2 v2.2.0 codeberg.org/gruf/go-fastcopy v1.1.2 - codeberg.org/gruf/go-iotools v0.0.0-20230601182242-d933b07dcbef + codeberg.org/gruf/go-iotools v0.0.0-20230811115124-5d4223615a7f codeberg.org/gruf/go-kv v1.6.4 codeberg.org/gruf/go-logger/v2 v2.2.1 - codeberg.org/gruf/go-mutexes v1.1.5 + codeberg.org/gruf/go-mutexes v1.2.0 codeberg.org/gruf/go-runners v1.6.1 codeberg.org/gruf/go-sched v1.2.3 - codeberg.org/gruf/go-store/v2 v2.2.2 + codeberg.org/gruf/go-store/v2 v2.2.4 github.com/DmitriyVTitov/size v1.5.0 github.com/KimMachineGun/automemlimit v0.3.0 github.com/abema/go-mp4 v1.1.1 @@ -77,12 +79,9 @@ require ( codeberg.org/gruf/go-atomics v1.1.0 // indirect codeberg.org/gruf/go-bitutil v1.1.0 // indirect codeberg.org/gruf/go-bytes v1.0.2 // indirect - codeberg.org/gruf/go-fastpath v1.0.3 // indirect codeberg.org/gruf/go-fastpath/v2 v2.0.0 // indirect - codeberg.org/gruf/go-hashenc v1.0.2 // indirect codeberg.org/gruf/go-mangler v1.2.3 // indirect codeberg.org/gruf/go-maps v1.0.3 // indirect - codeberg.org/gruf/go-pools v1.1.0 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/bytedance/sonic v1.9.1 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect @@ -128,7 +127,7 @@ require ( github.com/jinzhu/inflection v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect - github.com/klauspost/compress v1.16.7 // indirect + github.com/klauspost/compress v1.17.2 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/leodido/go-urn v1.2.4 // indirect github.com/magiconair/properties v1.8.7 // indirect diff --git a/go.sum b/go.sum index fffe18bad..ed6d88c9f 100644 --- a/go.sum +++ b/go.sum @@ -40,12 +40,10 @@ codeberg.org/gruf/go-atomics v1.1.0/go.mod h1:a/4/y/LgvjxjQVnpoy1VVkOSzLS1W9i1g4 codeberg.org/gruf/go-bitutil v1.0.0/go.mod h1:sb8IjlDnjVTz8zPK/8lmHesKxY0Yb3iqHWjUM/SkphA= codeberg.org/gruf/go-bitutil v1.1.0 h1:U1Q+A1mtnPk+npqYrlRBc9ar2C5hYiBd17l1Wrp2Bt8= codeberg.org/gruf/go-bitutil v1.1.0/go.mod h1:rGibFevYTQfYKcPv0Df5KpG8n5xC3AfD4d/UgYeoNy0= -codeberg.org/gruf/go-bytes v1.0.0/go.mod h1:1v/ibfaosfXSZtRdW2rWaVrDXMc9E3bsi/M9Ekx39cg= codeberg.org/gruf/go-bytes v1.0.2 h1:malqE42Ni+h1nnYWBUAJaDDtEzF4aeN4uPN8DfMNNvo= codeberg.org/gruf/go-bytes v1.0.2/go.mod h1:1v/ibfaosfXSZtRdW2rWaVrDXMc9E3bsi/M9Ekx39cg= codeberg.org/gruf/go-bytesize v1.0.2 h1:Mo+ITi+0uZ4YNSZf2ed6Qw8acOI39W4mmgE1a8lslXw= codeberg.org/gruf/go-bytesize v1.0.2/go.mod h1:n/GU8HzL9f3UNp/mUKyr1qVmTlj7+xacpp0OHfkvLPs= -codeberg.org/gruf/go-byteutil v1.0.0/go.mod h1:cWM3tgMCroSzqoBXUXMhvxTxYJp+TbCr6ioISRY5vSU= codeberg.org/gruf/go-byteutil v1.1.2 h1:TQLZtTxTNca9xEfDIndmo7nBYxeS94nrv/9DS3Nk5Tw= codeberg.org/gruf/go-byteutil v1.1.2/go.mod h1:cWM3tgMCroSzqoBXUXMhvxTxYJp+TbCr6ioISRY5vSU= codeberg.org/gruf/go-cache/v3 v3.5.6 h1:TJnNOuij5DF/ZK9pDB61SlYzxidRQeYjYYW3dfFSznc= @@ -57,15 +55,10 @@ codeberg.org/gruf/go-errors/v2 v2.2.0 h1:CxnTtR4+BqRGeBHuG/FdCKM4m3otMdfPVez6ReB codeberg.org/gruf/go-errors/v2 v2.2.0/go.mod h1:LfzD9nkAAJpEDbkUqOZQ2jdaQ8VrK0pnR36zLOMFq6Y= codeberg.org/gruf/go-fastcopy v1.1.2 h1:YwmYXPsyOcRBxKEE2+w1bGAZfclHVaPijFsOVOcnNcw= codeberg.org/gruf/go-fastcopy v1.1.2/go.mod h1:GDDYR0Cnb3U/AIfGM3983V/L+GN+vuwVMvrmVABo21s= -codeberg.org/gruf/go-fastpath v1.0.1/go.mod h1:edveE/Kp3Eqi0JJm0lXYdkVrB28cNUkcb/bRGFTPqeI= -codeberg.org/gruf/go-fastpath v1.0.3 h1:3Iftz9Z2suCEgTLkQMucew+2+4Oe46JPbAM2JEhnjTU= -codeberg.org/gruf/go-fastpath v1.0.3/go.mod h1:edveE/Kp3Eqi0JJm0lXYdkVrB28cNUkcb/bRGFTPqeI= codeberg.org/gruf/go-fastpath/v2 v2.0.0 h1:iAS9GZahFhyWEH0KLhFEJR+txx1ZhMXxYzu2q5Qo9c0= codeberg.org/gruf/go-fastpath/v2 v2.0.0/go.mod h1:3pPqu5nZjpbRrOqvLyAK7puS1OfEtQvjd6342Cwz56Q= -codeberg.org/gruf/go-hashenc v1.0.2 h1:U3jH6zMXZiL96czD/qaJd8OR2h7LlBzGv/2WxnMHI/g= -codeberg.org/gruf/go-hashenc v1.0.2/go.mod h1:eK+A8clLcEN/m1nftNsRId0kfYDQnETnuIfBGZ8Gvsg= -codeberg.org/gruf/go-iotools v0.0.0-20230601182242-d933b07dcbef h1:3Ydviw47TFEk27FRCOXkRxU3MfgyNzoicLzq8J3NbtI= -codeberg.org/gruf/go-iotools v0.0.0-20230601182242-d933b07dcbef/go.mod h1:B8uq4yHtIcKXhBZT9C/SYisz25lldLHMVpwZPz4ADLQ= +codeberg.org/gruf/go-iotools v0.0.0-20230811115124-5d4223615a7f h1:Kazm/PInN2m1SannRMRe3DQGQc9V2EuetsQ9KAi+pBQ= +codeberg.org/gruf/go-iotools v0.0.0-20230811115124-5d4223615a7f/go.mod h1:B8uq4yHtIcKXhBZT9C/SYisz25lldLHMVpwZPz4ADLQ= codeberg.org/gruf/go-kv v1.6.4 h1:3NZiW8HVdBM3kpOiLb7XfRiihnzZWMAixdCznguhILk= codeberg.org/gruf/go-kv v1.6.4/go.mod h1:O/YkSvKiS9XsRolM3rqCd9YJmND7dAXu9z+PrlYO4bc= codeberg.org/gruf/go-logger/v2 v2.2.1 h1:RP2u059EQKTBFV3cN8X6xDxNk2RkzqdgXGKflKqB7Oc= @@ -74,16 +67,12 @@ codeberg.org/gruf/go-mangler v1.2.3 h1:sj0dey2lF5GRQL7fXmCY0wPNaI5JrROiThb0VDbzF codeberg.org/gruf/go-mangler v1.2.3/go.mod h1:X/7URkFhLBAVKkTxmqF11Oxw3A6pSSxgPeHssQaiq28= codeberg.org/gruf/go-maps v1.0.3 h1:VDwhnnaVNUIy5O93CvkcE2IZXnMB1+IJjzfop9V12es= codeberg.org/gruf/go-maps v1.0.3/go.mod h1:D5LNDxlC9rsDuVQVM6JObaVGAdHB6g2dTdOdkh1aXWA= -codeberg.org/gruf/go-mutexes v1.1.5 h1:8Y8DwCGf24MyzOSaPvLrtk/B4ecVx4z+fppL6dY+PG8= -codeberg.org/gruf/go-mutexes v1.1.5/go.mod h1:1j/6/MBeBQUedAtAtysLLnBKogfOZAxdym0E3wlaBD8= -codeberg.org/gruf/go-pools v1.1.0 h1:LbYP24eQLl/YI1fSU2pafiwhGol1Z1zPjRrMsXpF88s= -codeberg.org/gruf/go-pools v1.1.0/go.mod h1:ZMYpt/DjQWYC3zFD3T97QWSFKs62zAUGJ/tzvgB9D68= codeberg.org/gruf/go-runners v1.6.1 h1:0KNiEfGnmNUs9intqxEAWqIKUyxVOmYTtn3kPVOHsjQ= codeberg.org/gruf/go-runners v1.6.1/go.mod h1:QRcSExqXX8DM0rm8Xs6qX7baOzyvw0JIe4mu3TsQT+Y= codeberg.org/gruf/go-sched v1.2.3 h1:H5ViDxxzOBR3uIyGBCf0eH8b1L8wMybOXcdtUUTXZHk= codeberg.org/gruf/go-sched v1.2.3/go.mod h1:vT9uB6KWFIIwnG9vcPY2a0alYNoqdL1mSzRM8I+PK7A= -codeberg.org/gruf/go-store/v2 v2.2.2 h1:S+OpXKXtPpkxJ7GyFZgJISXjXtGtz70//ee/uOi/6ks= -codeberg.org/gruf/go-store/v2 v2.2.2/go.mod h1:QRM3LUAfYyoGMWLTqA1WzohxQgYqPFiVv9cqwL0+Uvs= +codeberg.org/gruf/go-store/v2 v2.2.4 h1:8HO1Jh2gg7boQKA3hsDAIXd9zwieu5uXwDXEcTOD9js= +codeberg.org/gruf/go-store/v2 v2.2.4/go.mod h1:zI4VWe5CpXAktYMtaBMrgA5QmO0sQH53LBRvfn1huys= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= @@ -366,8 +355,8 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:C 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.10/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4= +github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= @@ -581,9 +570,6 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68= github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= -github.com/zeebo/blake3 v0.2.1/go.mod h1:TSQ0KjMH+pht+bRyvVooJ1rBpvvngSGaPISafq9MxJk= -github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -762,7 +748,6 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201014080544-cc95f250f6bc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -773,7 +758,6 @@ golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/internal/db/bundb/migrations/20220612091800_duplicated_media_cleanup.go b/internal/db/bundb/migrations/20220612091800_duplicated_media_cleanup.go index 688878c0e..28bbb3a81 100644 --- a/internal/db/bundb/migrations/20220612091800_duplicated_media_cleanup.go +++ b/internal/db/bundb/migrations/20220612091800_duplicated_media_cleanup.go @@ -23,7 +23,6 @@ import ( "fmt" "path" - "codeberg.org/gruf/go-store/v2/kv" "codeberg.org/gruf/go-store/v2/storage" "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" @@ -32,14 +31,14 @@ import ( ) func init() { - deleteAttachment := func(ctx context.Context, l log.Entry, a *gtsmodel.MediaAttachment, s *kv.KVStore, tx bun.Tx) { - if err := s.Delete(ctx, a.File.Path); err != nil && err != storage.ErrNotFound { + deleteAttachment := func(ctx context.Context, l log.Entry, a *gtsmodel.MediaAttachment, s storage.Storage, tx bun.Tx) { + if err := s.Remove(ctx, a.File.Path); err != nil && err != storage.ErrNotFound { l.Errorf("error removing file %s: %s", a.File.Path, err) } else { l.Debugf("deleted %s", a.File.Path) } - if err := s.Delete(ctx, a.Thumbnail.Path); err != nil && err != storage.ErrNotFound { + if err := s.Remove(ctx, a.Thumbnail.Path); err != nil && err != storage.ErrNotFound { l.Errorf("error removing file %s: %s", a.Thumbnail.Path, err) } else { l.Debugf("deleted %s", a.Thumbnail.Path) @@ -69,7 +68,7 @@ func init() { } return db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error { - s, err := kv.OpenDisk(storageBasePath, &storage.DiskConfig{ + s, err := storage.OpenDisk(storageBasePath, &storage.DiskConfig{ LockFile: path.Join(storageBasePath, "store.lock"), }) if err != nil { diff --git a/internal/federation/dereferencing/account.go b/internal/federation/dereferencing/account.go index 670c8e2c8..58f07f9cd 100644 --- a/internal/federation/dereferencing/account.go +++ b/internal/federation/dereferencing/account.go @@ -122,7 +122,7 @@ func (d *Dereferencer) getAccountByURI(ctx context.Context, requestUser string, } // Create and pass-through a new bare-bones model for dereferencing. - return d.enrichAccount(ctx, requestUser, uri, >smodel.Account{ + return d.enrichAccountSafely(ctx, requestUser, uri, >smodel.Account{ ID: id.NewULID(), Domain: uri.Host, URI: uriStr, @@ -139,7 +139,7 @@ func (d *Dereferencer) getAccountByURI(ctx context.Context, requestUser string, } // Try to update existing account model. - latest, apubAcc, err := d.enrichAccount(ctx, + latest, apubAcc, err := d.enrichAccountSafely(ctx, requestUser, uri, account, @@ -148,10 +148,6 @@ func (d *Dereferencer) getAccountByURI(ctx context.Context, requestUser string, if err != nil { log.Errorf(ctx, "error enriching remote account: %v", err) - // Update fetch-at to slow re-attempts. - account.FetchedAt = time.Now() - _ = d.state.DB.UpdateAccount(ctx, account, "fetched_at") - // Fallback to existing. return account, nil, nil } @@ -218,7 +214,7 @@ func (d *Dereferencer) getAccountByUsernameDomain( } // Create and pass-through a new bare-bones model for dereferencing. - account, apubAcc, err := d.enrichAccount(ctx, requestUser, nil, >smodel.Account{ + account, apubAcc, err := d.enrichAccountSafely(ctx, requestUser, nil, >smodel.Account{ ID: id.NewULID(), Username: username, Domain: domain, @@ -244,7 +240,7 @@ func (d *Dereferencer) getAccountByUsernameDomain( if apubAcc == nil { // This is existing up-to-date account, ensure it is populated. - if err := d.state.DB.PopulateAccount(ctx, account); err != nil { + if err := d.state.DB.PopulateAccount(ctx, latest); err != nil { log.Errorf(ctx, "error populating existing account: %v", err) } } @@ -267,8 +263,8 @@ func (d *Dereferencer) RefreshAccount(ctx context.Context, requestUser string, a return nil, nil, gtserror.Newf("invalid account uri %q: %w", account.URI, err) } - // Try to update + deref existing account model. - latest, apubAcc, err := d.enrichAccount(ctx, + // Try to update + deref passed account model. + latest, apubAcc, err := d.enrichAccountSafely(ctx, requestUser, uri, account, @@ -276,20 +272,17 @@ func (d *Dereferencer) RefreshAccount(ctx context.Context, requestUser string, a ) if err != nil { log.Errorf(ctx, "error enriching remote account: %v", err) - - // Update fetch-at to slow re-attempts. - account.FetchedAt = time.Now() - _ = d.state.DB.UpdateAccount(ctx, account, "fetched_at") - - return nil, nil, err + return nil, nil, gtserror.Newf("error enriching remote account: %w", err) } - // This account was updated, enqueue re-dereference featured posts. - d.state.Workers.Federator.MustEnqueueCtx(ctx, func(ctx context.Context) { - if err := d.dereferenceAccountFeatured(ctx, requestUser, account); err != nil { - log.Errorf(ctx, "error fetching account featured collection: %v", err) - } - }) + if apubAcc != nil { + // This account was updated, enqueue re-dereference featured posts. + d.state.Workers.Federator.MustEnqueueCtx(ctx, func(ctx context.Context) { + if err := d.dereferenceAccountFeatured(ctx, requestUser, latest); err != nil { + log.Errorf(ctx, "error fetching account featured collection: %v", err) + } + }) + } return latest, apubAcc, nil } @@ -311,21 +304,94 @@ func (d *Dereferencer) RefreshAccountAsync(ctx context.Context, requestUser stri // Enqueue a worker function to enrich this account async. d.state.Workers.Federator.MustEnqueueCtx(ctx, func(ctx context.Context) { - latest, _, err := d.enrichAccount(ctx, requestUser, uri, account, apubAcc) + latest, apubAcc, err := d.enrichAccountSafely(ctx, requestUser, uri, account, apubAcc) if err != nil { log.Errorf(ctx, "error enriching remote account: %v", err) return } - // This account was updated, re-dereference account featured posts. - if err := d.dereferenceAccountFeatured(ctx, requestUser, latest); err != nil { - log.Errorf(ctx, "error fetching account featured collection: %v", err) + if apubAcc != nil { + // This account was updated, enqueue re-dereference featured posts. + d.state.Workers.Federator.MustEnqueueCtx(ctx, func(ctx context.Context) { + if err := d.dereferenceAccountFeatured(ctx, requestUser, latest); err != nil { + log.Errorf(ctx, "error fetching account featured collection: %v", err) + } + }) } }) } -// enrichAccount will enrich the given account, whether a new barebones model, or existing model from the database. It handles necessary dereferencing, webfingering etc. -func (d *Dereferencer) enrichAccount(ctx context.Context, requestUser string, uri *url.URL, account *gtsmodel.Account, apubAcc ap.Accountable) (*gtsmodel.Account, ap.Accountable, error) { +// enrichAccountSafely wraps enrichAccount() to perform +// it within the State{}.FedLocks mutexmap, which protects +// dereferencing actions with per-URI mutex locks. +func (d *Dereferencer) enrichAccountSafely( + ctx context.Context, + requestUser string, + uri *url.URL, + account *gtsmodel.Account, + apubAcc ap.Accountable, +) (*gtsmodel.Account, ap.Accountable, error) { + // By default use account.URI + // as the per-URI deref lock. + uriStr := account.URI + + if uriStr == "" { + // No URI is set yet, instead generate a faux-one from user+domain. + uriStr = "https://" + account.Domain + "/user/" + account.Username + } + + // Acquire per-URI deref lock, wraping unlock + // to safely defer in case of panic, while still + // performing more granular unlocks when needed. + unlock := d.state.FedLocks.Lock(uriStr) + unlock = doOnce(unlock) + defer unlock() + + // Perform status enrichment with passed vars. + latest, apubAcc, err := d.enrichAccount(ctx, + requestUser, + uri, + account, + apubAcc, + ) + + if gtserror.StatusCode(err) >= 400 { + // Update fetch-at to slow re-attempts. + account.FetchedAt = time.Now() + _ = d.state.DB.UpdateAccount(ctx, account, "fetched_at") + } + + // Unlock now + // we're done. + unlock() + + if errors.Is(err, db.ErrAlreadyExists) { + // Ensure AP model isn't set, + // otherwise this indicates WE + // enriched the account. + apubAcc = nil + + // DATA RACE! We likely lost out to another goroutine + // in a call to db.Put(Account). Look again in DB by URI. + latest, err = d.state.DB.GetAccountByURI(ctx, account.URI) + if err != nil { + err = gtserror.Newf("error getting account %s from database after race: %w", uriStr, err) + } + } + + return latest, apubAcc, err +} + +// enrichAccount will enrich the given account, whether a +// new barebones model, or existing model from the database. +// It handles necessary dereferencing, webfingering etc. +func (d *Dereferencer) enrichAccount( + ctx context.Context, + requestUser string, + uri *url.URL, + account *gtsmodel.Account, + apubAcc ap.Accountable, +) (*gtsmodel.Account, ap.Accountable, error) { // Pre-fetch a transport for requesting username, used by later deref procedures. tsport, err := d.transportController.NewTransportForUsername(ctx, requestUser) if err != nil { @@ -476,13 +542,6 @@ func (d *Dereferencer) enrichAccount(ctx context.Context, requestUser string, ur // This is new, put it in the database. err := d.state.DB.PutAccount(ctx, latestAcc) - - if errors.Is(err, db.ErrAlreadyExists) { - // TODO: replace this quick fix with per-URI deref locks. - latestAcc, err = d.state.DB.GetAccountByURI(ctx, latestAcc.URI) - return latestAcc, nil, err - } - if err != nil { return nil, nil, gtserror.Newf("error putting in database: %w", err) } @@ -545,7 +604,8 @@ func (d *Dereferencer) fetchRemoteAccountAvatar(ctx context.Context, tsport tran } // Acquire lock for derefs map. - unlock := d.derefAvatarsMu.Lock() + unlock := d.state.FedLocks.Lock(latestAcc.AvatarRemoteURL) + unlock = doOnce(unlock) defer unlock() // Look for an existing dereference in progress. @@ -573,7 +633,7 @@ func (d *Dereferencer) fetchRemoteAccountAvatar(ctx context.Context, tsport tran defer func() { // On exit safely remove media from map. - unlock := d.derefAvatarsMu.Lock() + unlock := d.state.FedLocks.Lock(latestAcc.AvatarRemoteURL) delete(d.derefAvatars, latestAcc.AvatarRemoteURL) unlock() }() @@ -635,7 +695,8 @@ func (d *Dereferencer) fetchRemoteAccountHeader(ctx context.Context, tsport tran } // Acquire lock for derefs map. - unlock := d.derefHeadersMu.Lock() + unlock := d.state.FedLocks.Lock(latestAcc.HeaderRemoteURL) + unlock = doOnce(unlock) defer unlock() // Look for an existing dereference in progress. @@ -663,7 +724,7 @@ func (d *Dereferencer) fetchRemoteAccountHeader(ctx context.Context, tsport tran defer func() { // On exit safely remove media from map. - unlock := d.derefHeadersMu.Lock() + unlock := d.state.FedLocks.Lock(latestAcc.HeaderRemoteURL) delete(d.derefHeaders, latestAcc.HeaderRemoteURL) unlock() }() diff --git a/internal/federation/dereferencing/dereferencer.go b/internal/federation/dereferencing/dereferencer.go index a5c68bd80..5bd16c1e0 100644 --- a/internal/federation/dereferencing/dereferencer.go +++ b/internal/federation/dereferencing/dereferencer.go @@ -21,7 +21,6 @@ import ( "net/url" "sync" - "codeberg.org/gruf/go-mutexes" "github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/transport" @@ -35,14 +34,14 @@ type Dereferencer struct { converter *typeutils.Converter transportController transport.Controller mediaManager *media.Manager - derefAvatars map[string]*media.ProcessingMedia - derefAvatarsMu mutexes.Mutex - derefHeaders map[string]*media.ProcessingMedia - derefHeadersMu mutexes.Mutex - derefEmojis map[string]*media.ProcessingEmoji - derefEmojisMu mutexes.Mutex - handshakes map[string][]*url.URL - handshakesMu sync.Mutex // mutex to lock/unlock when checking or updating the handshakes map + + // all protected by State{}.FedLocks. + derefAvatars map[string]*media.ProcessingMedia + derefHeaders map[string]*media.ProcessingMedia + derefEmojis map[string]*media.ProcessingEmoji + + handshakes map[string][]*url.URL + handshakesMu sync.Mutex } // NewDereferencer returns a Dereferencer initialized with the given parameters. @@ -61,11 +60,5 @@ func NewDereferencer( derefHeaders: make(map[string]*media.ProcessingMedia), derefEmojis: make(map[string]*media.ProcessingEmoji), handshakes: make(map[string][]*url.URL), - - // use wrapped mutexes to allow safely deferring unlock - // even when more granular locks are required (only unlocks once). - derefAvatarsMu: mutexes.WithSafety(mutexes.New()), - derefHeadersMu: mutexes.WithSafety(mutexes.New()), - derefEmojisMu: mutexes.WithSafety(mutexes.New()), } } diff --git a/internal/federation/dereferencing/emoji.go b/internal/federation/dereferencing/emoji.go index 2d86da663..1bf19d2fd 100644 --- a/internal/federation/dereferencing/emoji.go +++ b/internal/federation/dereferencing/emoji.go @@ -36,8 +36,15 @@ func (d *Dereferencer) GetRemoteEmoji(ctx context.Context, requestingUsername st processingEmoji *media.ProcessingEmoji ) + // Ensure we have been passed a valid URL. + derefURI, err := url.Parse(remoteURL) + if err != nil { + return nil, fmt.Errorf("GetRemoteEmoji: error parsing url for emoji %s: %s", shortcodeDomain, err) + } + // Acquire lock for derefs map. - unlock := d.derefEmojisMu.Lock() + unlock := d.state.FedLocks.Lock(remoteURL) + unlock = doOnce(unlock) defer unlock() // first check if we're already processing this emoji @@ -51,11 +58,6 @@ func (d *Dereferencer) GetRemoteEmoji(ctx context.Context, requestingUsername st return nil, fmt.Errorf("GetRemoteEmoji: error creating transport to fetch emoji %s: %s", shortcodeDomain, err) } - derefURI, err := url.Parse(remoteURL) - if err != nil { - return nil, fmt.Errorf("GetRemoteEmoji: error parsing url for emoji %s: %s", shortcodeDomain, err) - } - dataFunc := func(innerCtx context.Context) (io.ReadCloser, int64, error) { return t.DereferenceMedia(innerCtx, derefURI) } @@ -75,7 +77,7 @@ func (d *Dereferencer) GetRemoteEmoji(ctx context.Context, requestingUsername st defer func() { // On exit safely remove emoji from map. - unlock := d.derefEmojisMu.Lock() + unlock := d.state.FedLocks.Lock(remoteURL) delete(d.derefEmojis, shortcodeDomain) unlock() }() @@ -95,7 +97,6 @@ func (d *Dereferencer) populateEmojis(ctx context.Context, rawEmojis []*gtsmodel // * the shortcode of the emoji // * the remote URL of the image // This should be enough to dereference the emoji - gotEmojis := make([]*gtsmodel.Emoji, 0, len(rawEmojis)) for _, e := range rawEmojis { diff --git a/internal/federation/dereferencing/status.go b/internal/federation/dereferencing/status.go index adc73e843..712692814 100644 --- a/internal/federation/dereferencing/status.go +++ b/internal/federation/dereferencing/status.go @@ -115,7 +115,7 @@ func (d *Dereferencer) getStatusByURI(ctx context.Context, requestUser string, u } // Create and pass-through a new bare-bones model for deref. - return d.enrichStatus(ctx, requestUser, uri, >smodel.Status{ + return d.enrichStatusSafely(ctx, requestUser, uri, >smodel.Status{ Local: func() *bool { var false bool; return &false }(), URI: uriStr, }, nil) @@ -131,7 +131,7 @@ func (d *Dereferencer) getStatusByURI(ctx context.Context, requestUser string, u } // Try to update + deref existing status model. - latest, apubStatus, err := d.enrichStatus(ctx, + latest, apubStatus, err := d.enrichStatusSafely(ctx, requestUser, uri, status, @@ -140,10 +140,6 @@ func (d *Dereferencer) getStatusByURI(ctx context.Context, requestUser string, u if err != nil { log.Errorf(ctx, "error enriching remote status: %v", err) - // Update fetch-at to slow re-attempts. - status.FetchedAt = time.Now() - _ = d.state.DB.UpdateStatus(ctx, status, "fetched_at") - // Fallback to existing. return status, nil, nil } @@ -166,8 +162,8 @@ func (d *Dereferencer) RefreshStatus(ctx context.Context, requestUser string, st return nil, nil, gtserror.Newf("invalid status uri %q: %w", status.URI, err) } - // Try to update + deref existing status model. - latest, apubStatus, err := d.enrichStatus(ctx, + // Try to update + deref the passed status model. + latest, apubStatus, err := d.enrichStatusSafely(ctx, requestUser, uri, status, @@ -189,7 +185,7 @@ func (d *Dereferencer) RefreshStatus(ctx context.Context, requestUser string, st // This is a more optimized form of manually enqueueing .UpdateStatus() to the federation worker, since it only enqueues update if necessary. func (d *Dereferencer) RefreshStatusAsync(ctx context.Context, requestUser string, status *gtsmodel.Status, apubStatus ap.Statusable, force bool) { // Check whether needs update. - if statusUpToDate(status) { + if !force && statusUpToDate(status) { return } @@ -202,17 +198,81 @@ func (d *Dereferencer) RefreshStatusAsync(ctx context.Context, requestUser strin // Enqueue a worker function to re-fetch this status async. d.state.Workers.Federator.MustEnqueueCtx(ctx, func(ctx context.Context) { - latest, apubStatus, err := d.enrichStatus(ctx, requestUser, uri, status, apubStatus) + latest, apubStatus, err := d.enrichStatusSafely(ctx, requestUser, uri, status, apubStatus) if err != nil { log.Errorf(ctx, "error enriching remote status: %v", err) return } - // This status was updated, re-dereference the whole thread. - d.dereferenceThread(ctx, requestUser, uri, latest, apubStatus) + if apubStatus != nil { + // This status was updated, re-dereference the whole thread. + d.dereferenceThread(ctx, requestUser, uri, latest, apubStatus) + } }) } +// enrichStatusSafely wraps enrichStatus() to perform +// it within the State{}.FedLocks mutexmap, which protects +// dereferencing actions with per-URI mutex locks. +func (d *Dereferencer) enrichStatusSafely( + ctx context.Context, + requestUser string, + uri *url.URL, + status *gtsmodel.Status, + apubStatus ap.Statusable, +) (*gtsmodel.Status, ap.Statusable, error) { + uriStr := status.URI + + if status.ID != "" { + // This is an existing status, first try to populate it. This + // is required by the checks below for existing tags, media etc. + if err := d.state.DB.PopulateStatus(ctx, status); err != nil { + log.Errorf(ctx, "error populating existing status %s: %v", uriStr, err) + } + } + + // Acquire per-URI deref lock, wraping unlock + // to safely defer in case of panic, while still + // performing more granular unlocks when needed. + unlock := d.state.FedLocks.Lock(uriStr) + unlock = doOnce(unlock) + defer unlock() + + // Perform status enrichment with passed vars. + latest, apubStatus, err := d.enrichStatus(ctx, + requestUser, + uri, + status, + apubStatus, + ) + + if gtserror.StatusCode(err) >= 400 { + // Update fetch-at to slow re-attempts. + status.FetchedAt = time.Now() + _ = d.state.DB.UpdateStatus(ctx, status, "fetched_at") + } + + // Unlock now + // we're done. + unlock() + + if errors.Is(err, db.ErrAlreadyExists) { + // Ensure AP model isn't set, + // otherwise this indicates WE + // enriched the status. + apubStatus = nil + + // DATA RACE! We likely lost out to another goroutine + // in a call to db.Put(Status). Look again in DB by URI. + latest, err = d.state.DB.GetStatusByURI(ctx, status.URI) + if err != nil { + err = gtserror.Newf("error getting status %s from database after race: %w", uriStr, err) + } + } + + return latest, apubStatus, err +} + // enrichStatus will enrich the given status, whether a new // barebones model, or existing model from the database. // It handles necessary dereferencing, database updates, etc. @@ -258,15 +318,10 @@ func (d *Dereferencer) enrichStatus( return nil, nil, gtserror.New("attributedTo was empty") } - // Ensure we have the author account of the status dereferenced (+ up-to-date). - if author, _, err := d.getAccountByURI(ctx, requestUser, attributedTo); err != nil { - if status.AccountID == "" { - // Provided status account is nil, i.e. this is a new status / author, so a deref fail is unrecoverable. - return nil, nil, gtserror.Newf("failed to dereference status author %s: %w", uri, err) - } - } else if status.AccountID != "" && status.AccountID != author.ID { - // There already existed an account for this status author, but account ID changed. This shouldn't happen! - log.Warnf(ctx, "status author account ID changed: old=%s new=%s", status.AccountID, author.ID) + // Ensure we have the author account of the status dereferenced (+ up-to-date). If this is a new status + // (i.e. status.AccountID == "") then any error here is irrecoverable. AccountID must ALWAYS be set. + if _, _, err := d.getAccountByURI(ctx, requestUser, attributedTo); err != nil && status.AccountID == "" { + return nil, nil, gtserror.Newf("failed to dereference status author %s: %w", uri, err) } // ActivityPub model was recently dereferenced, so assume that passed status @@ -303,7 +358,7 @@ func (d *Dereferencer) enrichStatus( } // Ensure the status' tags are populated, (changes are expected / okay). - if err := d.fetchStatusTags(ctx, latestStatus); err != nil { + if err := d.fetchStatusTags(ctx, status, latestStatus); err != nil { return nil, nil, gtserror.Newf("error populating tags for status %s: %w", uri, err) } @@ -323,13 +378,6 @@ func (d *Dereferencer) enrichStatus( // // This is new, put the status in the database. err := d.state.DB.PutStatus(ctx, latestStatus) - - if errors.Is(err, db.ErrAlreadyExists) { - // TODO: replace this quick fix with per-URI deref locks. - latestStatus, err = d.state.DB.GetStatusByURI(ctx, latestStatus.URI) - return latestStatus, nil, err - } - if err != nil { return nil, nil, gtserror.Newf("error putting in database: %w", err) } @@ -545,36 +593,41 @@ func (d *Dereferencer) threadStatus(ctx context.Context, status *gtsmodel.Status return nil } -func (d *Dereferencer) fetchStatusTags(ctx context.Context, status *gtsmodel.Status) error { +func (d *Dereferencer) fetchStatusTags(ctx context.Context, existing, status *gtsmodel.Status) error { // Allocate new slice to take the yet-to-be determined tag IDs. status.TagIDs = make([]string, len(status.Tags)) for i := range status.Tags { - placeholder := status.Tags[i] + tag := status.Tags[i] - // Look for existing tag with this name first. - tag, err := d.state.DB.GetTagByName(ctx, placeholder.Name) - if err != nil && !errors.Is(err, db.ErrNoEntries) { - log.Errorf(ctx, "db error getting tag %s: %v", tag.Name, err) + // Look for tag in existing status with name. + existing, ok := existing.GetTagByName(tag.Name) + if ok && existing.ID != "" { + status.Tags[i] = existing + status.TagIDs[i] = existing.ID continue } - if tag == nil { - // Create new ID for tag name. - tag = >smodel.Tag{ - ID: id.NewULID(), - Name: placeholder.Name, - } - - // Insert this tag with new name into the database. - if err := d.state.DB.PutTag(ctx, tag); err != nil { - log.Errorf(ctx, "db error putting tag %s: %v", tag.Name, err) - continue - } + // Look for existing tag with name in the database. + existing, err := d.state.DB.GetTagByName(ctx, tag.Name) + if err != nil && !errors.Is(err, db.ErrNoEntries) { + return gtserror.Newf("db error getting tag %s: %w", tag.Name, err) + } else if existing != nil { + status.Tags[i] = existing + status.TagIDs[i] = existing.ID + continue } - // Set the *new* tag and ID. - status.Tags[i] = tag + // Create new ID for tag. + tag.ID = id.NewULID() + + // Insert this tag with new name into the database. + if err := d.state.DB.PutTag(ctx, tag); err != nil { + log.Errorf(ctx, "db error putting tag %s: %v", tag.Name, err) + continue + } + + // Set new tag ID in slice. status.TagIDs[i] = tag.ID } @@ -600,10 +653,10 @@ func (d *Dereferencer) fetchStatusAttachments(ctx context.Context, tsport transp status.AttachmentIDs = make([]string, len(status.Attachments)) for i := range status.Attachments { - placeholder := status.Attachments[i] + attachment := status.Attachments[i] // Look for existing media attachment with remoet URL first. - existing, ok := existing.GetAttachmentByRemoteURL(placeholder.RemoteURL) + existing, ok := existing.GetAttachmentByRemoteURL(attachment.RemoteURL) if ok && existing.ID != "" && *existing.Cached { status.Attachments[i] = existing status.AttachmentIDs[i] = existing.ID @@ -611,9 +664,9 @@ func (d *Dereferencer) fetchStatusAttachments(ctx context.Context, tsport transp } // Ensure a valid media attachment remote URL. - remoteURL, err := url.Parse(placeholder.RemoteURL) + remoteURL, err := url.Parse(attachment.RemoteURL) if err != nil { - log.Errorf(ctx, "invalid remote media url %q: %v", placeholder.RemoteURL, err) + log.Errorf(ctx, "invalid remote media url %q: %v", attachment.RemoteURL, err) continue } @@ -622,9 +675,9 @@ func (d *Dereferencer) fetchStatusAttachments(ctx context.Context, tsport transp return tsport.DereferenceMedia(ctx, remoteURL) }, status.AccountID, &media.AdditionalMediaInfo{ StatusID: &status.ID, - RemoteURL: &placeholder.RemoteURL, - Description: &placeholder.Description, - Blurhash: &placeholder.Blurhash, + RemoteURL: &attachment.RemoteURL, + Description: &attachment.Description, + Blurhash: &attachment.Blurhash, }) if err != nil { log.Errorf(ctx, "error processing attachment: %v", err) @@ -632,15 +685,15 @@ func (d *Dereferencer) fetchStatusAttachments(ctx context.Context, tsport transp } // Force attachment loading *right now*. - media, err := processing.LoadAttachment(ctx) + attachment, err = processing.LoadAttachment(ctx) if err != nil { log.Errorf(ctx, "error loading attachment: %v", err) continue } // Set the *new* attachment and ID. - status.Attachments[i] = media - status.AttachmentIDs[i] = media.ID + status.Attachments[i] = attachment + status.AttachmentIDs[i] = attachment.ID } for i := 0; i < len(status.AttachmentIDs); { diff --git a/internal/federation/dereferencing/error.go b/internal/federation/dereferencing/util.go similarity index 83% rename from internal/federation/dereferencing/error.go rename to internal/federation/dereferencing/util.go index 6a1ce0a6e..e69aeec3b 100644 --- a/internal/federation/dereferencing/error.go +++ b/internal/federation/dereferencing/util.go @@ -16,3 +16,14 @@ // along with this program. If not, see . package dereferencing + +// doOnce wraps a function to only perform it once. +func doOnce(fn func()) func() { + var once int32 + return func() { + if once == 0 { + fn() + once = 1 + } + } +} diff --git a/internal/federation/federatingdb/db.go b/internal/federation/federatingdb/db.go index c412ba3f8..8e98dc2f2 100644 --- a/internal/federation/federatingdb/db.go +++ b/internal/federation/federatingdb/db.go @@ -20,7 +20,6 @@ package federatingdb import ( "context" - "codeberg.org/gruf/go-mutexes" "github.com/superseriousbusiness/activity/pub" "github.com/superseriousbusiness/activity/streams/vocab" "github.com/superseriousbusiness/gotosocial/internal/state" @@ -40,7 +39,6 @@ type DB interface { // FederatingDB uses the underlying DB interface to implement the go-fed pub.Database interface. // It doesn't care what the underlying implementation of the DB interface is, as long as it works. type federatingDB struct { - locks mutexes.MutexMap state *state.State converter *typeutils.Converter } @@ -48,7 +46,6 @@ type federatingDB struct { // New returns a DB interface using the given database and config func New(state *state.State, converter *typeutils.Converter) DB { fdb := federatingDB{ - locks: mutexes.NewMap(-1, -1), // use defaults state: state, converter: converter, } diff --git a/internal/federation/federatingdb/lock.go b/internal/federation/federatingdb/lock.go index 900a282af..5353aea91 100644 --- a/internal/federation/federatingdb/lock.go +++ b/internal/federation/federatingdb/lock.go @@ -19,7 +19,6 @@ package federatingdb import ( "context" - "errors" "net/url" ) @@ -35,9 +34,5 @@ import ( // // Used to ensure race conditions in multiple requests do not occur. func (f *federatingDB) Lock(c context.Context, id *url.URL) (func(), error) { - if id == nil { - return nil, errors.New("Lock: id was nil") - } - unlock := f.locks.Lock(id.String()) - return unlock, nil + return f.state.FedLocks.Lock("federatingDB " + id.String()), nil // id should NEVER be nil. } diff --git a/internal/gtsmodel/status.go b/internal/gtsmodel/status.go index fe8aa4a7b..81b5b029d 100644 --- a/internal/gtsmodel/status.go +++ b/internal/gtsmodel/status.go @@ -18,11 +18,8 @@ package gtsmodel import ( - "time" - "slices" - - "github.com/superseriousbusiness/gotosocial/internal/log" + "time" ) // Status represents a user-created 'post' or 'status' in the database, either remote or local @@ -91,40 +88,14 @@ func (s *Status) GetBoostOfAccountID() string { return s.BoostOfAccountID } -func (s *Status) GetAttachmentByID(id string) (*MediaAttachment, bool) { - for _, media := range s.Attachments { - if media == nil { - log.Warnf(nil, "nil attachment in slice for status %s", s.URI) - continue - } - if media.ID == id { - return media, true - } - } - return nil, false -} - -func (s *Status) GetAttachmentByRemoteURL(url string) (*MediaAttachment, bool) { - for _, media := range s.Attachments { - if media == nil { - log.Warnf(nil, "nil attachment in slice for status %s", s.URI) - continue - } - if media.RemoteURL == url { - return media, true - } - } - return nil, false -} - // AttachmentsPopulated returns whether media attachments are populated according to current AttachmentIDs. func (s *Status) AttachmentsPopulated() bool { if len(s.AttachmentIDs) != len(s.Attachments) { // this is the quickest indicator. return false } - for _, id := range s.AttachmentIDs { - if _, ok := s.GetAttachmentByID(id); !ok { + for i, id := range s.AttachmentIDs { + if s.Attachments[i].ID != id { return false } } @@ -137,55 +108,22 @@ func (s *Status) TagsPopulated() bool { // this is the quickest indicator. return false } - - // Tags must be in same order. for i, id := range s.TagIDs { - if s.Tags[i] == nil { - log.Warnf(nil, "nil tag in slice for status %s", s.URI) - continue - } if s.Tags[i].ID != id { return false } } - return true } -func (s *Status) GetMentionByID(id string) (*Mention, bool) { - for _, mention := range s.Mentions { - if mention == nil { - log.Warnf(nil, "nil mention in slice for status %s", s.URI) - continue - } - if mention.ID == id { - return mention, true - } - } - return nil, false -} - -func (s *Status) GetMentionByTargetURI(uri string) (*Mention, bool) { - for _, mention := range s.Mentions { - if mention == nil { - log.Warnf(nil, "nil mention in slice for status %s", s.URI) - continue - } - if mention.TargetAccountURI == uri { - return mention, true - } - } - return nil, false -} - // MentionsPopulated returns whether mentions are populated according to current MentionIDs. func (s *Status) MentionsPopulated() bool { if len(s.MentionIDs) != len(s.Mentions) { // this is the quickest indicator. return false } - for _, id := range s.MentionIDs { - if _, ok := s.GetMentionByID(id); !ok { + for i, id := range s.MentionIDs { + if s.Mentions[i].ID != id { return false } } @@ -198,18 +136,11 @@ func (s *Status) EmojisPopulated() bool { // this is the quickest indicator. return false } - - // Emojis must be in same order. for i, id := range s.EmojiIDs { - if s.Emojis[i] == nil { - log.Warnf(nil, "nil emoji in slice for status %s", s.URI) - continue - } if s.Emojis[i].ID != id { return false } } - return true } @@ -221,28 +152,44 @@ func (s *Status) EmojisUpToDate(other *Status) bool { // this is the quickest indicator. return false } - - // Emojis must be in same order. for i := range s.Emojis { - if s.Emojis[i] == nil { - log.Warnf(nil, "nil emoji in slice for status %s", s.URI) - return false - } - - if other.Emojis[i] == nil { - log.Warnf(nil, "nil emoji in slice for status %s", other.URI) - return false - } - if s.Emojis[i].URI != other.Emojis[i].URI { - // Emoji URI has changed, not up-to-date! return false } } - return true } +// GetAttachmentByRemoteURL searches status for MediaAttachment{} with remote URL. +func (s *Status) GetAttachmentByRemoteURL(url string) (*MediaAttachment, bool) { + for _, media := range s.Attachments { + if media.RemoteURL == url { + return media, true + } + } + return nil, false +} + +// GetMentionByTargetURI searches status for Mention{} with target URI. +func (s *Status) GetMentionByTargetURI(uri string) (*Mention, bool) { + for _, mention := range s.Mentions { + if mention.TargetAccountURI == uri { + return mention, true + } + } + return nil, false +} + +// GetTagByName searches status for Tag{} with name. +func (s *Status) GetTagByName(name string) (*Tag, bool) { + for _, tag := range s.Tags { + if tag.Name == name { + return tag, true + } + } + return nil, false +} + // MentionsAccount returns whether status mentions the given account ID. func (s *Status) MentionsAccount(accountID string) bool { return slices.ContainsFunc(s.Mentions, func(m *Mention) bool { diff --git a/internal/state/state.go b/internal/state/state.go index 6ff1baa52..7cd0406b0 100644 --- a/internal/state/state.go +++ b/internal/state/state.go @@ -18,6 +18,7 @@ package state import ( + "codeberg.org/gruf/go-mutexes" "github.com/superseriousbusiness/gotosocial/internal/cache" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/storage" @@ -41,6 +42,11 @@ type State struct { // DB provides access to the database. DB db.DB + // FedLocks provides access to this state's mutex map + // of per URI federation locks. Used during dereferencing + // and by the go-fed/activity library. + FedLocks mutexes.MutexMap + // Storage provides access to the storage driver. Storage *storage.Driver diff --git a/vendor/codeberg.org/gruf/go-fastpath/LICENSE b/vendor/codeberg.org/gruf/go-fastpath/LICENSE deleted file mode 100644 index b7c4417ac..000000000 --- a/vendor/codeberg.org/gruf/go-fastpath/LICENSE +++ /dev/null @@ -1,9 +0,0 @@ -MIT License - -Copyright (c) 2021 gruf - -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. diff --git a/vendor/codeberg.org/gruf/go-fastpath/README.md b/vendor/codeberg.org/gruf/go-fastpath/README.md deleted file mode 100644 index f89700fb3..000000000 --- a/vendor/codeberg.org/gruf/go-fastpath/README.md +++ /dev/null @@ -1 +0,0 @@ -Alternative path library with a `strings.Builder` like path builder. \ No newline at end of file diff --git a/vendor/codeberg.org/gruf/go-fastpath/benchmarks.png b/vendor/codeberg.org/gruf/go-fastpath/benchmarks.png deleted file mode 100644 index 42ddc6d1eaaf888050584d3d589b148ff31d9bbc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 108279 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

aDDr%SN7%2; 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 diff --git a/vendor/codeberg.org/gruf/go-fastpath/path.go b/vendor/codeberg.org/gruf/go-fastpath/path.go deleted file mode 100644 index d8f434fe6..000000000 --- a/vendor/codeberg.org/gruf/go-fastpath/path.go +++ /dev/null @@ -1,319 +0,0 @@ -package fastpath - -import ( - "unsafe" -) - -// allocate these just once -var ( - dot = []byte(dotStr) - dotStr = "." -) - -type Builder struct { - B []byte // B is the underlying byte buffer - dd int // pos of last '..' appended to builder - abs bool // abs stores whether path passed to first .Append() is absolute - set bool // set stores whether b.abs has been set i.e. not first call to .Append() -} - -// NewBuilder returns a new Builder object using the -// supplied byte slice as the underlying buffer -func NewBuilder(b []byte) Builder { - if b != nil { - b = b[:0] - } - return Builder{ - B: b, - dd: 0, - - abs: false, - set: false, - } -} - -// Reset resets the Builder object -func (b *Builder) Reset() { - b.B = b.B[:0] - b.dd = 0 - b.abs = false - b.set = false -} - -// Len returns the number of accumulated bytes in the Builder -func (b *Builder) Len() int { - return len(b.B) -} - -// Cap returns the capacity of the underlying Builder buffer -func (b *Builder) Cap() int { - return cap(b.B) -} - -// Bytes returns the accumulated path bytes. -func (b *Builder) Bytes() []byte { - if len(b.B) < 1 { - return dot - } - return b.B -} - -// String returns the accumulated path string. -func (b *Builder) String() string { - if len(b.B) < 1 { - return dotStr - } - return string(b.B) -} - -// StringPtr returns a ptr to the accumulated path string. -// -// Please note the underlying byte slice for this string is -// tied to the builder, so any changes will result in the -// returned string changing. Consider using .String() if -// this is undesired behaviour. -func (b *Builder) StringPtr() string { - if len(b.B) < 1 { - return dotStr - } - return *(*string)(unsafe.Pointer(&b.B)) -} - -// Absolute returns whether current path is absolute (not relative) -func (b *Builder) Absolute() bool { - return b.abs -} - -// SetAbsolute converts the current path to / from absolute -func (b *Builder) SetAbsolute(val bool) { - if !b.set { - if val { - // .Append() has not been - // called, add a '/' and set abs - b.Guarantee(1) - b.appendByte('/') - b.abs = true - } - - // Set as having been set - b.set = true - return - } - - if !val && b.abs { - // Already set and absolute. Update - b.abs = false - - // If not empty (i.e. not just '/'), - // then shift bytes 1 left - if len(b.B) > 1 { - copy(b.B, b.B[1:]) - } - - // Truncate 1 byte. In the case of empty, - // i.e. just '/' then it will drop this - b.truncate(1) - } else if val && !b.abs { - // Already set but NOT abs. Update - b.abs = true - - // Guarantee 1 byte available - b.Guarantee(1) - - // If empty, just append '/' - if len(b.B) < 1 { - b.appendByte('/') - return - } - - // Increase length - l := len(b.B) - b.B = b.B[:l+1] - - // Shift bytes 1 right - copy(b.B[1:], b.B[:l]) - - // Set first byte '/' - b.B[0] = '/' - } -} - -// Append adds and cleans the supplied path bytes to the -// builder's internal buffer, growing the buffer if necessary -// to accomodate the extra path length -func (b *Builder) Append(p []byte) { - b.AppendString(*(*string)(unsafe.Pointer(&p))) -} - -// AppendString adds and cleans the supplied path string to the -// builder's internal buffer, growing the buffer if necessary -// to accomodate the extra path length -func (b *Builder) AppendString(path string) { - defer func() { - // If buffer is empty, and an absolute - // path, ensure it starts with a '/' - if len(b.B) < 1 && b.abs { - b.appendByte('/') - } - }() - - // Empty path, nothing to do - if len(path) == 0 { - return - } - - // Guarantee at least the total length - // of supplied path available in the buffer - b.Guarantee(len(path)) - - // Try store if absolute - if !b.set { - b.abs = len(path) > 0 && path[0] == '/' - b.set = true - } - - i := 0 - for i < len(path) { - switch { - // Empty path segment - case path[i] == '/': - i++ - - // Singular '.' path segment, treat as empty - case path[i] == '.' && (i+1 == len(path) || path[i+1] == '/'): - i++ - - // Backtrack segment - case path[i] == '.' && path[i+1] == '.' && (i+2 == len(path) || path[i+2] == '/'): - i += 2 - - switch { - // Check if it's possible to backtrack with - // our current state of the buffer. i.e. is - // our buffer length longer than the last - // '..' we placed? - case len(b.B) > b.dd: - b.backtrack() - // b.cp = b.lp - // b.lp = 0 - - // If we reached here, need to check if - // we can append '..' to the path buffer, - // which is ONLY when path is NOT absolute - case !b.abs: - if len(b.B) > 0 { - b.appendByte('/') - } - b.appendByte('.') - b.appendByte('.') - b.dd = len(b.B) - // b.lp = lp - 2 - // b.cp = b.dd - } - - default: - if (b.abs && len(b.B) != 1) || (!b.abs && len(b.B) > 0) { - b.appendByte('/') - } - // b.lp = b.cp - // b.cp = len(b.B) - i += b.appendSlice(path[i:]) - } - } -} - -// Clean creates the shortest possible functional equivalent -// to the supplied path, resetting the builder before performing -// this operation. The builder object is NOT reset after return -func (b *Builder) Clean(path string) string { - b.Reset() - b.AppendString(path) - return b.String() -} - -// Join connects and cleans multiple paths, resetting the builder before -// performing this operation and returning the shortest possible combination -// of all the supplied paths. The builder object is NOT reset after return -func (b *Builder) Join(base string, paths ...string) string { - b.Reset() - b.AppendString(base) - size := len(base) - for i := 0; i < len(paths); i++ { - b.AppendString(paths[i]) - size += len(paths[i]) - } - if size < 1 { - return "" - } else if len(b.B) < 1 { - return dotStr - } - return string(b.B) -} - -// Guarantee ensures there is at least the requested size -// free bytes available in the buffer, reallocating if necessary -func (b *Builder) Guarantee(size int) { - if size > cap(b.B)-len(b.B) { - nb := make([]byte, 2*cap(b.B)+size) - copy(nb, b.B) - b.B = nb[:len(b.B)] - } -} - -// Truncate reduces the length of the buffer by the requested -// number of bytes. If the builder is set to absolute, the first -// byte (i.e. '/') will never be truncated -func (b *Builder) Truncate(size int) { - // If absolute and just '/', do nothing - if b.abs && len(b.B) == 1 { - return - } - - // Truncate requested bytes - b.truncate(size) -} - -// truncate reduces the length of the buffer by the requested -// size, no sanity checks are performed -func (b *Builder) truncate(size int) { - b.B = b.B[:len(b.B)-size] -} - -// appendByte appends the supplied byte to the end of -// the buffer. appending is achieved by continually reslicing the -// buffer and setting the next byte-at-index, this is safe as guarantee() -// will have been called beforehand -func (b *Builder) appendByte(c byte) { - b.B = b.B[:len(b.B)+1] - b.B[len(b.B)-1] = c -} - -// appendSlice appends the supplied string slice to -// the end of the buffer and returns the number of indices -// we were able to iterate before hitting a path separator '/'. -// appending is achieved by continually reslicing the buffer -// and setting the next byte-at-index, this is safe as guarantee() -// will have been called beforehand -func (b *Builder) appendSlice(slice string) int { - i := 0 - for i < len(slice) && slice[i] != '/' { - b.B = b.B[:len(b.B)+1] - b.B[len(b.B)-1] = slice[i] - i++ - } - return i -} - -// backtrack reduces the end of the buffer back to the last -// separating '/', or end of buffer -func (b *Builder) backtrack() { - b.B = b.B[:len(b.B)-1] - - for len(b.B)-1 > b.dd && b.B[len(b.B)-1] != '/' { - b.B = b.B[:len(b.B)-1] - } - - if len(b.B) > 0 { - b.B = b.B[:len(b.B)-1] - } -} diff --git a/vendor/codeberg.org/gruf/go-hashenc/LICENSE b/vendor/codeberg.org/gruf/go-hashenc/LICENSE deleted file mode 100644 index b7c4417ac..000000000 --- a/vendor/codeberg.org/gruf/go-hashenc/LICENSE +++ /dev/null @@ -1,9 +0,0 @@ -MIT License - -Copyright (c) 2021 gruf - -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. diff --git a/vendor/codeberg.org/gruf/go-hashenc/README.md b/vendor/codeberg.org/gruf/go-hashenc/README.md deleted file mode 100644 index e885d0bb2..000000000 --- a/vendor/codeberg.org/gruf/go-hashenc/README.md +++ /dev/null @@ -1 +0,0 @@ -HashEncoder provides a means of quickly hash-summing and encoding data \ No newline at end of file diff --git a/vendor/codeberg.org/gruf/go-hashenc/enc.go b/vendor/codeberg.org/gruf/go-hashenc/enc.go deleted file mode 100644 index 0cc8d5986..000000000 --- a/vendor/codeberg.org/gruf/go-hashenc/enc.go +++ /dev/null @@ -1,42 +0,0 @@ -package hashenc - -import ( - "encoding/base32" - "encoding/base64" - "encoding/hex" -) - -// Encoder defines an interface for encoding binary data. -type Encoder interface { - // Encode encodes the data at src into dst - Encode(dst []byte, src []byte) - - // EncodedLen returns the encoded length for input data of supplied length - EncodedLen(int) int -} - -// Base32 returns a new base32 Encoder (StdEncoding, no padding). -func Base32() Encoder { - return base32.StdEncoding.WithPadding(base64.NoPadding) -} - -// Base64 returns a new base64 Encoder (URLEncoding, no padding). -func Base64() Encoder { - return base64.URLEncoding.WithPadding(base64.NoPadding) -} - -// Hex returns a new hex Encoder. -func Hex() Encoder { - return &hexEncoder{} -} - -// hexEncoder simply provides an empty receiver to satisfy Encoder. -type hexEncoder struct{} - -func (*hexEncoder) Encode(dst []byte, src []byte) { - hex.Encode(dst, src) -} - -func (*hexEncoder) EncodedLen(len int) int { - return hex.EncodedLen(len) -} diff --git a/vendor/codeberg.org/gruf/go-hashenc/hashenc.go b/vendor/codeberg.org/gruf/go-hashenc/hashenc.go deleted file mode 100644 index fc110c533..000000000 --- a/vendor/codeberg.org/gruf/go-hashenc/hashenc.go +++ /dev/null @@ -1,58 +0,0 @@ -package hashenc - -import ( - "hash" - - "codeberg.org/gruf/go-bytes" -) - -// HashEncoder defines an interface for calculating encoded hash sums of binary data -type HashEncoder interface { - // EncodeSum calculates the hash sum of src and encodes (at most) Size() into dst - EncodeSum(dst []byte, src []byte) - - // EncodedSum calculates the encoded hash sum of src and returns data in a newly allocated bytes.Bytes - EncodedSum(src []byte) bytes.Bytes - - // Size returns the expected length of encoded hashes - Size() int -} - -// New returns a new HashEncoder instance based on supplied hash.Hash and Encoder supplying functions. -func New(hash hash.Hash, enc Encoder) HashEncoder { - hashSize := hash.Size() - return &henc{ - hash: hash, - hbuf: make([]byte, hashSize), - enc: enc, - size: enc.EncodedLen(hashSize), - } -} - -// henc is the HashEncoder implementation. -type henc struct { - hash hash.Hash - hbuf []byte - enc Encoder - size int -} - -func (henc *henc) EncodeSum(dst []byte, src []byte) { - // Hash supplied bytes - henc.hash.Reset() - henc.hash.Write(src) - henc.hbuf = henc.hash.Sum(henc.hbuf[:0]) - - // Encode the hashsum and return a copy - henc.enc.Encode(dst, henc.hbuf) -} - -func (henc *henc) EncodedSum(src []byte) bytes.Bytes { - dst := make([]byte, henc.size) - henc.EncodeSum(dst, src) - return bytes.ToBytes(dst) -} - -func (henc *henc) Size() int { - return henc.size -} diff --git a/vendor/codeberg.org/gruf/go-iotools/read.go b/vendor/codeberg.org/gruf/go-iotools/read.go index 4a134e7b3..6ce2789a7 100644 --- a/vendor/codeberg.org/gruf/go-iotools/read.go +++ b/vendor/codeberg.org/gruf/go-iotools/read.go @@ -12,6 +12,14 @@ func (r ReaderFunc) Read(b []byte) (int, error) { return r(b) } +// ReaderFromFunc is a function signature which allows +// a function to implement the io.ReaderFrom type. +type ReaderFromFunc func(io.Reader) (int64, error) + +func (rf ReaderFromFunc) ReadFrom(r io.Reader) (int64, error) { + return rf(r) +} + // ReadCloser wraps an io.Reader and io.Closer in order to implement io.ReadCloser. func ReadCloser(r io.Reader, c io.Closer) io.ReadCloser { return &struct { diff --git a/vendor/codeberg.org/gruf/go-iotools/write.go b/vendor/codeberg.org/gruf/go-iotools/write.go index c520b8636..e1b44db24 100644 --- a/vendor/codeberg.org/gruf/go-iotools/write.go +++ b/vendor/codeberg.org/gruf/go-iotools/write.go @@ -10,6 +10,14 @@ func (w WriterFunc) Write(b []byte) (int, error) { return w(b) } +// WriterToFunc is a function signature which allows +// a function to implement the io.WriterTo type. +type WriterToFunc func(io.Writer) (int64, error) + +func (wt WriterToFunc) WriteTo(r io.Writer) (int64, error) { + return wt(r) +} + // WriteCloser wraps an io.Writer and io.Closer in order to implement io.WriteCloser. func WriteCloser(w io.Writer, c io.Closer) io.WriteCloser { return &struct { diff --git a/vendor/codeberg.org/gruf/go-mutexes/LICENSE b/vendor/codeberg.org/gruf/go-mutexes/LICENSE index b7c4417ac..d6f08d0ab 100644 --- a/vendor/codeberg.org/gruf/go-mutexes/LICENSE +++ b/vendor/codeberg.org/gruf/go-mutexes/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 gruf +Copyright (c) gruf 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: diff --git a/vendor/codeberg.org/gruf/go-mutexes/map.go b/vendor/codeberg.org/gruf/go-mutexes/map.go index 73f8f1821..6fcd9c9b1 100644 --- a/vendor/codeberg.org/gruf/go-mutexes/map.go +++ b/vendor/codeberg.org/gruf/go-mutexes/map.go @@ -1,9 +1,8 @@ package mutexes import ( - "runtime" "sync" - "sync/atomic" + "unsafe" ) const ( @@ -12,452 +11,253 @@ const ( lockTypeWrite = uint8(1) << 1 lockTypeMap = uint8(1) << 2 - // possible mutexmap states. - stateUnlockd = uint8(0) - stateRLocked = uint8(1) - stateLocked = uint8(2) - stateInUse = uint8(3) - - // default values. - defaultWake = 1024 + // frequency of GC cycles + // per no. unlocks. i.e. + // every 'gcfreq' unlocks. + gcfreq = 1024 ) -// acquireState attempts to acquire required map state for lockType. -func acquireState(state uint8, lt uint8) (uint8, bool) { - switch state { - // Unlocked state - // (all allowed) - case stateUnlockd: - - // Keys locked, no state lock. - // (don't allow map locks) - case stateInUse: - if lt&lockTypeMap != 0 { - return 0, false - } - - // Read locked - // (only allow read locks) - case stateRLocked: - if lt&lockTypeRead == 0 { - return 0, false - } - - // Write locked - // (none allowed) - case stateLocked: - return 0, false - - // shouldn't reach here - default: - panic("unexpected state") - } - - switch { - // If unlocked and not a map - // lock request, set in use - case lt&lockTypeMap == 0: - if state == stateUnlockd { - state = stateInUse - } - - // Set read lock state - case lt&lockTypeRead != 0: - state = stateRLocked - - // Set write lock state - case lt&lockTypeWrite != 0: - state = stateLocked - - default: - panic("unexpected lock type") - } - - return state, true -} - -// MutexMap is a structure that allows read / write locking key, performing -// as you'd expect a map[string]*sync.RWMutex to perform. The differences -// being that the entire map can itself be read / write locked, it uses memory -// pooling for the mutex (not quite) structures, and it is self-evicting. The -// core configurations of maximum no. open locks and wake modulus* are user -// definable. +// MutexMap is a structure that allows read / write locking +// per key, performing as you'd expect a map[string]*RWMutex +// to perform, without you needing to worry about deadlocks +// between competing read / write locks and the map's own mutex. +// It uses memory pooling for the internal "mutex" (ish) types +// and performs self-eviction of keys. // -// * The wake modulus is the number that the current number of open locks is -// modulused against to determine how often to notify sleeping goroutines. -// These are goroutines that are attempting to lock a key / whole map and are -// awaiting a permissible state (.e.g no key write locks allowed when the -// map is read locked). +// Under the hood this is achieved using a single mutex for the +// map, state tracking for individual keys, and some simple waitgroup +// type structures to park / block goroutines waiting for keys. type MutexMap struct { - queue *sync.WaitGroup - qucnt int32 - - mumap map[string]*rwmutex - mpool pool - evict []*rwmutex - - count int32 - maxmu int32 - wake int32 - - mapmu sync.Mutex - state uint8 + mapmu sync.Mutex + mumap map[string]*rwmutexish + mupool rwmutexPool + count uint32 } -// NewMap returns a new MutexMap instance with provided max no. open mutexes. -func NewMap(max, wake int32) MutexMap { - // Determine wake mod. - if wake < 1 { - wake = defaultWake - } - - // Determine max no. mutexes - if max < 1 { - procs := runtime.GOMAXPROCS(0) - max = wake * int32(procs) - } - - return MutexMap{ - queue: &sync.WaitGroup{}, - mumap: make(map[string]*rwmutex, max), - maxmu: max, - wake: wake, +// checkInit ensures MutexMap is initialized (UNSAFE). +func (mm *MutexMap) checkInit() { + if mm.mumap == nil { + mm.mumap = make(map[string]*rwmutexish) } } -// SET sets the MutexMap max open locks and wake modulus, returns current values. -// For values less than zero defaults are set, and zero is non-op. -func (mm *MutexMap) SET(max, wake int32) (int32, int32) { - mm.mapmu.Lock() - - switch { - // Set default wake - case wake < 0: - mm.wake = defaultWake - - // Set supplied wake - case wake > 0: - mm.wake = wake - } - - switch { - // Set default max - case max < 0: - procs := runtime.GOMAXPROCS(0) - mm.maxmu = wake * int32(procs) - - // Set supplied max - case max > 0: - mm.maxmu = max - } - - // Fetch values - max = mm.maxmu - wake = mm.wake - - mm.mapmu.Unlock() - return max, wake -} - -// spinLock will wait (using a mutex to sleep thread) until conditional returns true. -func (mm *MutexMap) spinLock(cond func() bool) { - for { - // Acquire map lock - mm.mapmu.Lock() - - if cond() { - return - } - - // Current queue ptr - queue := mm.queue - - // Queue ourselves - queue.Add(1) - mm.qucnt++ - - // Unlock map - mm.mapmu.Unlock() - - // Wait on notify - mm.queue.Wait() - } -} - -// lock will acquire a lock of given type on the 'mutex' at key. -func (mm *MutexMap) lock(key string, lt uint8) func() { - var ok bool - var mu *rwmutex - - // Spin lock until returns true - mm.spinLock(func() bool { - // Check not overloaded - if !(mm.count < mm.maxmu) { - return false - } - - // Attempt to acquire usable map state - state, ok := acquireState(mm.state, lt) - if !ok { - return false - } - - // Update state - mm.state = state - - // Ensure mutex at key - // is in lockable state - mu, ok = mm.mumap[key] - return !ok || mu.CanLock(lt) - }) - - // Incr count - mm.count++ - - if !ok { - // No mutex found for key - - // Alloc mu from pool - mu = mm.mpool.Acquire() - mm.mumap[key] = mu - - // Set our key - mu.key = key - - // Queue for eviction - mm.evict = append(mm.evict, mu) - } - - // Lock mutex - mu.Lock(lt) - - // Unlock map - mm.mapmu.Unlock() - - return func() { - mm.mapmu.Lock() - mu.Unlock() - mm.cleanup() - } -} - -// lockMap will lock the whole map under given lock type. -func (mm *MutexMap) lockMap(lt uint8) { - // Spin lock until returns true - mm.spinLock(func() bool { - // Attempt to acquire usable map state - state, ok := acquireState(mm.state, lt) - if !ok { - return false - } - - // Update state - mm.state = state - - return true - }) - - // Incr count - mm.count++ - - // State acquired, unlock - mm.mapmu.Unlock() -} - -// cleanup is performed as the final stage of unlocking a locked key / map state, finally unlocks map. -func (mm *MutexMap) cleanup() { - // Decr count - mm.count-- - - // Calculate current wake modulus - wakemod := mm.count % mm.wake - - if mm.count != 0 && wakemod != 0 { - // Fast path => no cleanup. - // Unlock, return early - mm.mapmu.Unlock() - return - } - - go func() { - if wakemod == 0 { - // Release queued goroutines - mm.queue.Add(-int(mm.qucnt)) - - // Allocate new queue and reset - mm.queue = &sync.WaitGroup{} - mm.qucnt = 0 - } - - if mm.count == 0 { - // Perform evictions - for _, mu := range mm.evict { - key := mu.key - mu.key = "" - delete(mm.mumap, key) - mm.mpool.Release(mu) - } - - // Reset map state - mm.evict = mm.evict[:0] - mm.state = stateUnlockd - mm.mpool.GC() - } - - // Unlock map - mm.mapmu.Unlock() - }() -} - -// RLockMap acquires a read lock over the entire map, returning a lock state for acquiring key read locks. -// Please note that the 'unlock()' function will block until all keys locked from this state are unlocked. -func (mm *MutexMap) RLockMap() *LockState { - mm.lockMap(lockTypeRead | lockTypeMap) - return &LockState{ - mmap: mm, - ltyp: lockTypeRead, - } -} - -// LockMap acquires a write lock over the entire map, returning a lock state for acquiring key read/write locks. -// Please note that the 'unlock()' function will block until all keys locked from this state are unlocked. -func (mm *MutexMap) LockMap() *LockState { - mm.lockMap(lockTypeWrite | lockTypeMap) - return &LockState{ - mmap: mm, - ltyp: lockTypeWrite, - } -} - -// RLock acquires a mutex read lock for supplied key, returning an RUnlock function. -func (mm *MutexMap) RLock(key string) (runlock func()) { - return mm.lock(key, lockTypeRead) -} - -// Lock acquires a mutex write lock for supplied key, returning an Unlock function. -func (mm *MutexMap) Lock(key string) (unlock func()) { +// Lock acquires a write lock on key in map, returning unlock function. +func (mm *MutexMap) Lock(key string) func() { return mm.lock(key, lockTypeWrite) } -// LockState represents a window to a locked MutexMap. -type LockState struct { - wait sync.WaitGroup - mmap *MutexMap - done uint32 - ltyp uint8 +// RLock acquires a read lock on key in map, returning runlock function. +func (mm *MutexMap) RLock(key string) func() { + return mm.lock(key, lockTypeRead) } -// Lock: see MutexMap.Lock() definition. Will panic if map only read locked. -func (st *LockState) Lock(key string) (unlock func()) { - return st.lock(key, lockTypeWrite) +func (mm *MutexMap) lock(key string, lt uint8) func() { + // Perform first map lock + // and check initialization + // OUTSIDE the main loop. + mm.mapmu.Lock() + mm.checkInit() + + for { + // Check map for mu. + mu := mm.mumap[key] + + if mu == nil { + // Allocate new mutex. + mu = mm.mupool.Acquire() + mm.mumap[key] = mu + } + + if !mu.Lock(lt) { + // Wait on mutex unlock, after + // immediately relocking map mu. + mu.WaitRelock(&mm.mapmu) + continue + } + + // Done with map. + mm.mapmu.Unlock() + + // Return mutex unlock function. + return func() { mm.unlock(key, mu) } + } } -// RLock: see MutexMap.RLock() definition. -func (st *LockState) RLock(key string) (runlock func()) { - return st.lock(key, lockTypeRead) -} +func (mm *MutexMap) unlock(key string, mu *rwmutexish) { + // Get map lock. + mm.mapmu.Lock() -// lock: see MutexMap.lock() definition. -func (st *LockState) lock(key string, lt uint8) func() { - st.wait.Add(1) // track lock + // Unlock mutex. + if mu.Unlock() { - if atomic.LoadUint32(&st.done) == 1 { - panic("called (r)lock on unlocked state") - } else if lt&lockTypeWrite != 0 && - st.ltyp&lockTypeWrite == 0 { - panic("called lock on rlocked map") + // Mutex fully unlocked + // with zero waiters. Self + // evict and release it. + delete(mm.mumap, key) + mm.mupool.Release(mu) } - var ok bool - var mu *rwmutex + if mm.count++; mm.count%gcfreq == 0 { + // Every 'gcfreq' unlocks perform + // a garbage collection to keep + // us squeaky clean :] + mm.mupool.GC() + } - // Spin lock until returns true - st.mmap.spinLock(func() bool { - // Check not overloaded - if !(st.mmap.count < st.mmap.maxmu) { + // Done with map. + mm.mapmu.Unlock() +} + +// rwmutexPool is a very simply memory rwmutexPool. +type rwmutexPool struct { + current []*rwmutexish + victim []*rwmutexish +} + +// Acquire will returns a rwmutexState from rwmutexPool (or alloc new). +func (p *rwmutexPool) Acquire() *rwmutexish { + // First try the current queue + if l := len(p.current) - 1; l >= 0 { + mu := p.current[l] + p.current = p.current[:l] + return mu + } + + // Next try the victim queue. + if l := len(p.victim) - 1; l >= 0 { + mu := p.victim[l] + p.victim = p.victim[:l] + return mu + } + + // Lastly, alloc new. + mu := new(rwmutexish) + return mu +} + +// Release places a sync.rwmutexState back in the rwmutexPool. +func (p *rwmutexPool) Release(mu *rwmutexish) { + p.current = append(p.current, mu) +} + +// GC will clear out unused entries from the rwmutexPool. +func (p *rwmutexPool) GC() { + current := p.current + p.current = nil + p.victim = current +} + +// rwmutexish is a RW mutex (ish), i.e. the representation +// of one only to be accessed within +type rwmutexish struct { + tr trigger + ln int32 // no. locks + wn int32 // no. waiters + lt uint8 // lock type +} + +// Lock will lock the mutex for given lock type, in the +// sense that it will update the internal state tracker +// accordingly. Return value is true on successful lock. +func (mu *rwmutexish) Lock(lt uint8) bool { + switch mu.lt { + case lockTypeRead: + // already read locked, + // only permit more reads. + if lt != lockTypeRead { return false } - // Ensure mutex at key - // is in lockable state - mu, ok = st.mmap.mumap[key] - return !ok || mu.CanLock(lt) - }) + case lockTypeWrite: + // already write locked, + // no other locks allowed. + return false - // Incr count - st.mmap.count++ - - if !ok { - // No mutex found for key - - // Alloc mu from pool - mu = st.mmap.mpool.Acquire() - st.mmap.mumap[key] = mu - - // Set our key - mu.key = key - - // Queue for eviction - st.mmap.evict = append(st.mmap.evict, mu) + default: + // Fully unlocked. + mu.lt = lt } - // Lock mutex - mu.Lock(lt) + // Update + // count. + mu.ln++ - // Unlock map - st.mmap.mapmu.Unlock() - - return func() { - st.mmap.mapmu.Lock() - mu.Unlock() - st.mmap.cleanup() - st.wait.Add(-1) - } + return true } -// UnlockMap will close this state and release the currently locked map. -func (st *LockState) UnlockMap() { - if !atomic.CompareAndSwapUint32(&st.done, 0, 1) { - panic("called unlockmap on expired state") - } - st.wait.Wait() - st.mmap.mapmu.Lock() - st.mmap.cleanup() -} +// Unlock will unlock the mutex, in the sense that +// it will update the internal state tracker accordingly. +// On any unlock it will awaken sleeping waiting threads. +// Returned boolean is if unlocked=true AND waiters=0. +func (mu *rwmutexish) Unlock() bool { + var ok bool -// rwmutex is a very simple *representation* of a read-write -// mutex, though not one in implementation. it works by -// tracking the lock state for a given map key, which is -// protected by the map's mutex. -type rwmutex struct { - rcnt int32 // read lock count - lock uint8 // lock type - key string // map key -} + switch mu.ln--; { + case mu.ln > 0 && mu.lt == lockTypeWrite: + panic("BUG: multiple writer locks") + case mu.ln < 0: + panic("BUG: negative lock count") + case mu.ln == 0: + // Fully unlocked. + mu.lt = 0 -func (mu *rwmutex) CanLock(lt uint8) bool { - return mu.lock == 0 || - (mu.lock&lockTypeRead != 0 && lt&lockTypeRead != 0) -} - -func (mu *rwmutex) Lock(lt uint8) { - // Set lock type - mu.lock = lt - - if lt&lockTypeRead != 0 { - // RLock, increment - mu.rcnt++ - } -} - -func (mu *rwmutex) Unlock() { - if mu.rcnt > 0 { - // RUnlock - mu.rcnt-- + // Only return true + // with no waiters. + ok = (mu.wn == 0) } - if mu.rcnt == 0 { - // Total unlock - mu.lock = 0 - } + // Awake all waiting + // goroutines for mu. + mu.tr.Trigger() + return ok } + +// WaitRelock expects a mutex to be passed in already in +// the lock state. It incr the rwmutexish waiter count before +// unlocking the outer mutex and blocking on internal trigger. +// On awake it will relock outer mutex and decr wait count. +func (mu *rwmutexish) WaitRelock(outer *sync.Mutex) { + mu.wn++ + outer.Unlock() + mu.tr.Wait() + outer.Lock() + mu.wn-- +} + +// trigger uses the internals of sync.Cond to provide +// a waitgroup type structure (including goroutine parks) +// without such a heavy reliance on a delta value. +type trigger struct{ notifyList } + +func (t *trigger) Trigger() { + runtime_notifyListNotifyAll(&t.notifyList) +} + +func (t *trigger) Wait() { + v := runtime_notifyListAdd(&t.notifyList) + runtime_notifyListWait(&t.notifyList, v) +} + +// Approximation of notifyList in runtime/sema.go. +type notifyList struct { + wait uint32 + notify uint32 + lock uintptr // key field of the mutex + head unsafe.Pointer + tail unsafe.Pointer +} + +// See runtime/sema.go for documentation. +// +//go:linkname runtime_notifyListAdd sync.runtime_notifyListAdd +func runtime_notifyListAdd(l *notifyList) uint32 + +// See runtime/sema.go for documentation. +// +//go:linkname runtime_notifyListWait sync.runtime_notifyListWait +func runtime_notifyListWait(l *notifyList, t uint32) + +// See runtime/sema.go for documentation. +// +//go:linkname runtime_notifyListNotifyAll sync.runtime_notifyListNotifyAll +func runtime_notifyListNotifyAll(l *notifyList) diff --git a/vendor/codeberg.org/gruf/go-mutexes/map_pool.go b/vendor/codeberg.org/gruf/go-mutexes/map_pool.go deleted file mode 100644 index 450e0bc06..000000000 --- a/vendor/codeberg.org/gruf/go-mutexes/map_pool.go +++ /dev/null @@ -1,39 +0,0 @@ -package mutexes - -// pool is a very simply memory pool. -type pool struct { - current []*rwmutex - victim []*rwmutex -} - -// Acquire will returns a rwmutex from pool (or alloc new). -func (p *pool) Acquire() *rwmutex { - // First try the current queue - if l := len(p.current) - 1; l >= 0 { - mu := p.current[l] - p.current = p.current[:l] - return mu - } - - // Next try the victim queue. - if l := len(p.victim) - 1; l >= 0 { - mu := p.victim[l] - p.victim = p.victim[:l] - return mu - } - - // Lastly, alloc new. - return &rwmutex{} -} - -// Release places a sync.RWMutex back in the pool. -func (p *pool) Release(mu *rwmutex) { - p.current = append(p.current, mu) -} - -// GC will clear out unused entries from the pool. -func (p *pool) GC() { - current := p.current - p.current = nil - p.victim = current -} diff --git a/vendor/codeberg.org/gruf/go-pools/LICENSE b/vendor/codeberg.org/gruf/go-pools/LICENSE deleted file mode 100644 index b7c4417ac..000000000 --- a/vendor/codeberg.org/gruf/go-pools/LICENSE +++ /dev/null @@ -1,9 +0,0 @@ -MIT License - -Copyright (c) 2021 gruf - -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. diff --git a/vendor/codeberg.org/gruf/go-pools/README.md b/vendor/codeberg.org/gruf/go-pools/README.md deleted file mode 100644 index 673324271..000000000 --- a/vendor/codeberg.org/gruf/go-pools/README.md +++ /dev/null @@ -1,2 +0,0 @@ -A selection of type-defined `sync.Pool` implementations with redefined "getter" and "putter" -methods to handle their appropriate types. \ No newline at end of file diff --git a/vendor/codeberg.org/gruf/go-pools/bufio.go b/vendor/codeberg.org/gruf/go-pools/bufio.go deleted file mode 100644 index e22fd6a1c..000000000 --- a/vendor/codeberg.org/gruf/go-pools/bufio.go +++ /dev/null @@ -1,89 +0,0 @@ -package pools - -import ( - "bufio" - "io" - "sync" -) - -// BufioReaderPool is a pooled allocator for bufio.Reader objects. -type BufioReaderPool interface { - // Get fetches a bufio.Reader from pool and resets to supplied reader - Get(io.Reader) *bufio.Reader - - // Put places supplied bufio.Reader back in pool - Put(*bufio.Reader) -} - -// NewBufioReaderPool returns a newly instantiated bufio.Reader pool. -func NewBufioReaderPool(size int) BufioReaderPool { - return &bufioReaderPool{ - pool: sync.Pool{ - New: func() interface{} { - return bufio.NewReaderSize(nil, size) - }, - }, - size: size, - } -} - -// bufioReaderPool is our implementation of BufioReaderPool. -type bufioReaderPool struct { - pool sync.Pool - size int -} - -func (p *bufioReaderPool) Get(r io.Reader) *bufio.Reader { - br := p.pool.Get().(*bufio.Reader) - br.Reset(r) - return br -} - -func (p *bufioReaderPool) Put(br *bufio.Reader) { - if br.Size() < p.size { - return - } - br.Reset(nil) - p.pool.Put(br) -} - -// BufioWriterPool is a pooled allocator for bufio.Writer objects. -type BufioWriterPool interface { - // Get fetches a bufio.Writer from pool and resets to supplied writer - Get(io.Writer) *bufio.Writer - - // Put places supplied bufio.Writer back in pool - Put(*bufio.Writer) -} - -// NewBufioWriterPool returns a newly instantiated bufio.Writer pool. -func NewBufioWriterPool(size int) BufioWriterPool { - return &bufioWriterPool{ - pool: sync.Pool{ - New: func() interface{} { - return bufio.NewWriterSize(nil, size) - }, - }, - size: size, - } -} - -// bufioWriterPool is our implementation of BufioWriterPool. -type bufioWriterPool struct { - pool sync.Pool - size int -} - -func (p *bufioWriterPool) Get(w io.Writer) *bufio.Writer { - bw := p.pool.Get().(*bufio.Writer) - bw.Reset(w) - return bw -} - -func (p *bufioWriterPool) Put(bw *bufio.Writer) { - if bw.Size() < p.size { - return - } - bw.Reset(nil) - p.pool.Put(bw) -} diff --git a/vendor/codeberg.org/gruf/go-pools/bytes.go b/vendor/codeberg.org/gruf/go-pools/bytes.go deleted file mode 100644 index 1aee77064..000000000 --- a/vendor/codeberg.org/gruf/go-pools/bytes.go +++ /dev/null @@ -1,46 +0,0 @@ -package pools - -import ( - "sync" - - "codeberg.org/gruf/go-byteutil" -) - -// BufferPool is a pooled allocator for bytes.Buffer objects -type BufferPool interface { - // Get fetches a bytes.Buffer from pool - Get() *byteutil.Buffer - - // Put places supplied bytes.Buffer in pool - Put(*byteutil.Buffer) -} - -// NewBufferPool returns a newly instantiated bytes.Buffer pool -func NewBufferPool(size int) BufferPool { - return &bufferPool{ - pool: sync.Pool{ - New: func() interface{} { - return &byteutil.Buffer{B: make([]byte, 0, size)} - }, - }, - size: size, - } -} - -// bufferPool is our implementation of BufferPool -type bufferPool struct { - pool sync.Pool - size int -} - -func (p *bufferPool) Get() *byteutil.Buffer { - return p.pool.Get().(*byteutil.Buffer) -} - -func (p *bufferPool) Put(buf *byteutil.Buffer) { - if buf.Cap() < p.size { - return - } - buf.Reset() - p.pool.Put(buf) -} diff --git a/vendor/codeberg.org/gruf/go-pools/fastpath.go b/vendor/codeberg.org/gruf/go-pools/fastpath.go deleted file mode 100644 index eb76f03e4..000000000 --- a/vendor/codeberg.org/gruf/go-pools/fastpath.go +++ /dev/null @@ -1,46 +0,0 @@ -package pools - -import ( - "sync" - - "codeberg.org/gruf/go-fastpath" -) - -// PathBuilderPool is a pooled allocator for fastpath.Builder objects -type PathBuilderPool interface { - // Get fetches a fastpath.Builder from pool - Get() *fastpath.Builder - - // Put places supplied fastpath.Builder back in pool - Put(*fastpath.Builder) -} - -// NewPathBuilderPool returns a newly instantiated fastpath.Builder pool -func NewPathBuilderPool(size int) PathBuilderPool { - return &pathBuilderPool{ - pool: sync.Pool{ - New: func() interface{} { - return &fastpath.Builder{B: make([]byte, 0, size)} - }, - }, - size: size, - } -} - -// pathBuilderPool is our implementation of PathBuilderPool -type pathBuilderPool struct { - pool sync.Pool - size int -} - -func (p *pathBuilderPool) Get() *fastpath.Builder { - return p.pool.Get().(*fastpath.Builder) -} - -func (p *pathBuilderPool) Put(pb *fastpath.Builder) { - if pb.Cap() < p.size { - return - } - pb.Reset() - p.pool.Put(pb) -} diff --git a/vendor/codeberg.org/gruf/go-pools/henc.go b/vendor/codeberg.org/gruf/go-pools/henc.go deleted file mode 100644 index cad905af4..000000000 --- a/vendor/codeberg.org/gruf/go-pools/henc.go +++ /dev/null @@ -1,46 +0,0 @@ -package pools - -import ( - "hash" - "sync" - - "codeberg.org/gruf/go-hashenc" -) - -// HashEncoderPool is a pooled allocator for hashenc.HashEncoder objects. -type HashEncoderPool interface { - // Get fetches a hashenc.HashEncoder from pool - Get() hashenc.HashEncoder - - // Put places supplied hashenc.HashEncoder back in pool - Put(hashenc.HashEncoder) -} - -// NewHashEncoderPool returns a newly instantiated hashenc.HashEncoder pool. -func NewHashEncoderPool(hash func() hash.Hash, enc func() hashenc.Encoder) HashEncoderPool { - return &hencPool{ - pool: sync.Pool{ - New: func() interface{} { - return hashenc.New(hash(), enc()) - }, - }, - size: hashenc.New(hash(), enc()).Size(), - } -} - -// hencPool is our implementation of HashEncoderPool. -type hencPool struct { - pool sync.Pool - size int -} - -func (p *hencPool) Get() hashenc.HashEncoder { - return p.pool.Get().(hashenc.HashEncoder) -} - -func (p *hencPool) Put(henc hashenc.HashEncoder) { - if henc.Size() < p.size { - return - } - p.pool.Put(henc) -} diff --git a/vendor/codeberg.org/gruf/go-pools/pool.go b/vendor/codeberg.org/gruf/go-pools/pool.go deleted file mode 100644 index 1e3db74b2..000000000 --- a/vendor/codeberg.org/gruf/go-pools/pool.go +++ /dev/null @@ -1,387 +0,0 @@ -package pools - -import ( - "runtime" - "sync" - "sync/atomic" - "unsafe" -) - -type Pool struct { - // New is used to instantiate new items - New func() interface{} - - // Evict is called on evicted items during pool .Clean() - Evict func(interface{}) - - local unsafe.Pointer // ptr to []_ppool - localSz int64 // count of all elems in local - victim unsafe.Pointer // ptr to []_ppool - victimSz int64 // count of all elems in victim - mutex sync.Mutex // mutex protects new cleanups, and new allocations of local -} - -// Get attempts to fetch an item from the pool, failing that allocates with supplied .New() function -func (p *Pool) Get() interface{} { - // Get local pool for proc - // (also pins proc) - pool, pid := p.pin() - - if v := pool.getPrivate(); v != nil { - // local _ppool private elem acquired - runtime_procUnpin() - atomic.AddInt64(&p.localSz, -1) - return v - } - - if v := pool.get(); v != nil { - // local _ppool queue elem acquired - runtime_procUnpin() - atomic.AddInt64(&p.localSz, -1) - return v - } - - // Unpin before attempting slow - runtime_procUnpin() - if v := p.getSlow(pid); v != nil { - // note size decrementing - // is handled within p.getSlow() - // as we don't know if it came - // from the local or victim pools - return v - } - - // Alloc new - return p.New() -} - -// Put places supplied item in the proc local pool -func (p *Pool) Put(v interface{}) { - // Don't store nil - if v == nil { - return - } - - // Get proc local pool - // (also pins proc) - pool, _ := p.pin() - - // first try private, then queue - if !pool.setPrivate(v) { - pool.put(v) - } - runtime_procUnpin() - - // Increment local pool size - atomic.AddInt64(&p.localSz, 1) -} - -// Clean will drop the current victim pools, move the current local pools to its -// place and reset the local pools ptr in order to be regenerated -func (p *Pool) Clean() { - p.mutex.Lock() - - // victim becomes local, local becomes nil - localPtr := atomic.SwapPointer(&p.local, nil) - victimPtr := atomic.SwapPointer(&p.victim, localPtr) - localSz := atomic.SwapInt64(&p.localSz, 0) - atomic.StoreInt64(&p.victimSz, localSz) - - var victim []ppool - if victimPtr != nil { - victim = *(*[]ppool)(victimPtr) - } - - // drain each of the vict _ppool items - for i := 0; i < len(victim); i++ { - ppool := &victim[i] - ppool.evict(p.Evict) - } - - p.mutex.Unlock() -} - -// LocalSize returns the total number of elements in all the proc-local pools -func (p *Pool) LocalSize() int64 { - return atomic.LoadInt64(&p.localSz) -} - -// VictimSize returns the total number of elements in all the victim (old proc-local) pools -func (p *Pool) VictimSize() int64 { - return atomic.LoadInt64(&p.victimSz) -} - -// getSlow is the slow path for fetching an element, attempting to steal from other proc's -// local pools, and failing that, from the aging-out victim pools. pid is still passed so -// not all procs start iterating from the same index -func (p *Pool) getSlow(pid int) interface{} { - // get local pools - local := p.localPools() - - // Try to steal from other proc locals - for i := 0; i < len(local); i++ { - pool := &local[(pid+i+1)%len(local)] - if v := pool.get(); v != nil { - atomic.AddInt64(&p.localSz, -1) - return v - } - } - - // get victim pools - victim := p.victimPools() - - // Attempt to steal from victim pools - for i := 0; i < len(victim); i++ { - pool := &victim[(pid+i+1)%len(victim)] - if v := pool.get(); v != nil { - atomic.AddInt64(&p.victimSz, -1) - return v - } - } - - // Set victim pools to nil (none found) - atomic.StorePointer(&p.victim, nil) - - return nil -} - -// localPools safely loads slice of local _ppools -func (p *Pool) localPools() []ppool { - local := atomic.LoadPointer(&p.local) - if local == nil { - return nil - } - return *(*[]ppool)(local) -} - -// victimPools safely loads slice of victim _ppools -func (p *Pool) victimPools() []ppool { - victim := atomic.LoadPointer(&p.victim) - if victim == nil { - return nil - } - return *(*[]ppool)(victim) -} - -// pin will get fetch pin proc to PID, fetch proc-local _ppool and current PID we're pinned to -func (p *Pool) pin() (*ppool, int) { - for { - // get local pools - local := p.localPools() - - if len(local) > 0 { - // local already initialized - - // pin to current proc - pid := runtime_procPin() - - // check for pid local pool - if pid < len(local) { - return &local[pid], pid - } - - // unpin from proc - runtime_procUnpin() - } else { - // local not yet initialized - - // Check functions are set - if p.New == nil { - panic("new func must not be nil") - } - if p.Evict == nil { - panic("evict func must not be nil") - } - } - - // allocate local - p.allocLocal() - } -} - -// allocLocal allocates a new local pool slice, with the old length passed to check -// if pool was previously nil, or whether a change in GOMAXPROCS occurred -func (p *Pool) allocLocal() { - // get pool lock - p.mutex.Lock() - - // Calculate new size to use - size := runtime.GOMAXPROCS(0) - - local := p.localPools() - if len(local) != size { - // GOMAXPROCS changed, reallocate - pools := make([]ppool, size) - atomic.StorePointer(&p.local, unsafe.Pointer(&pools)) - - // Evict old local elements - for i := 0; i < len(local); i++ { - pool := &local[i] - pool.evict(p.Evict) - } - } - - // Unlock pool - p.mutex.Unlock() -} - -// _ppool is a proc local pool -type _ppool struct { - // root is the root element of the _ppool queue, - // and protects concurrent access to the queue - root unsafe.Pointer - - // private is a proc private member accessible - // only to the pid this _ppool is assigned to, - // except during evict (hence the unsafe pointer) - private unsafe.Pointer -} - -// ppool wraps _ppool with pad. -type ppool struct { - _ppool - - // Prevents false sharing on widespread platforms with - // 128 mod (cache line size) = 0 . - pad [128 - unsafe.Sizeof(_ppool{})%128]byte -} - -// getPrivate gets the proc private member -func (pp *_ppool) getPrivate() interface{} { - ptr := atomic.SwapPointer(&pp.private, nil) - if ptr == nil { - return nil - } - return *(*interface{})(ptr) -} - -// setPrivate sets the proc private member (only if unset) -func (pp *_ppool) setPrivate(v interface{}) bool { - return atomic.CompareAndSwapPointer(&pp.private, nil, unsafe.Pointer(&v)) -} - -// get fetches an element from the queue -func (pp *_ppool) get() interface{} { - for { - // Attempt to load root elem - root := atomic.LoadPointer(&pp.root) - if root == nil { - return nil - } - - // Attempt to consume root elem - if root == inUsePtr || - !atomic.CompareAndSwapPointer(&pp.root, root, inUsePtr) { - continue - } - - // Root becomes next in chain - e := (*elem)(root) - v := e.value - - // Place new root back in the chain - atomic.StorePointer(&pp.root, unsafe.Pointer(e.next)) - putElem(e) - - return v - } -} - -// put places an element in the queue -func (pp *_ppool) put(v interface{}) { - // Prepare next elem - e := getElem() - e.value = v - - for { - // Attempt to load root elem - root := atomic.LoadPointer(&pp.root) - if root == inUsePtr { - continue - } - - // Set the next elem value (might be nil) - e.next = (*elem)(root) - - // Attempt to store this new value at root - if atomic.CompareAndSwapPointer(&pp.root, root, unsafe.Pointer(e)) { - break - } - } -} - -// hook evicts all entries from pool, calling hook on each -func (pp *_ppool) evict(hook func(interface{})) { - if v := pp.getPrivate(); v != nil { - hook(v) - } - for { - v := pp.get() - if v == nil { - break - } - hook(v) - } -} - -// inUsePtr is a ptr used to indicate _ppool is in use -var inUsePtr = unsafe.Pointer(&elem{ - next: nil, - value: "in_use", -}) - -// elem defines an element in the _ppool queue -type elem struct { - next *elem - value interface{} -} - -// elemPool is a simple pool of unused elements -var elemPool = struct { - root unsafe.Pointer -}{} - -// getElem fetches a new elem from pool, or creates new -func getElem() *elem { - // Attempt to load root elem - root := atomic.LoadPointer(&elemPool.root) - if root == nil { - return &elem{} - } - - // Attempt to consume root elem - if root == inUsePtr || - !atomic.CompareAndSwapPointer(&elemPool.root, root, inUsePtr) { - return &elem{} - } - - // Root becomes next in chain - e := (*elem)(root) - atomic.StorePointer(&elemPool.root, unsafe.Pointer(e.next)) - e.next = nil - - return e -} - -// putElem will place element in the pool -func putElem(e *elem) { - e.value = nil - - // Attempt to load root elem - root := atomic.LoadPointer(&elemPool.root) - if root == inUsePtr { - return // drop - } - - // Set the next elem value (might be nil) - e.next = (*elem)(root) - - // Attempt to store this new value at root - atomic.CompareAndSwapPointer(&elemPool.root, root, unsafe.Pointer(e)) -} - -//go:linkname runtime_procPin sync.runtime_procPin -func runtime_procPin() int - -//go:linkname runtime_procUnpin sync.runtime_procUnpin -func runtime_procUnpin() diff --git a/vendor/codeberg.org/gruf/go-store/v2/kv/iterator.go b/vendor/codeberg.org/gruf/go-store/v2/kv/iterator.go deleted file mode 100644 index 736edddaa..000000000 --- a/vendor/codeberg.org/gruf/go-store/v2/kv/iterator.go +++ /dev/null @@ -1,63 +0,0 @@ -package kv - -import ( - "context" - "errors" - - "codeberg.org/gruf/go-mutexes" - "codeberg.org/gruf/go-store/v2/storage" -) - -var ErrIteratorClosed = errors.New("store/kv: iterator closed") - -// Iterator provides a read-only iterator to all the key-value -// pairs in a KVStore. While the iterator is open the store is read -// locked, you MUST release the iterator when you are finished with -// it. -// -// Please note: -// individual iterators are NOT concurrency safe, though it is safe to -// have multiple iterators running concurrently. -type Iterator struct { - store *KVStore // store is the linked KVStore - state *mutexes.LockState - entries []storage.Entry - index int - key string -} - -// Next attempts to fetch the next key-value pair, the -// return value indicates whether another pair remains. -func (i *Iterator) Next() bool { - next := i.index + 1 - if next >= len(i.entries) { - i.key = "" - return false - } - i.key = i.entries[next].Key - i.index = next - return true -} - -// Key returns the current iterator key. -func (i *Iterator) Key() string { - return i.key -} - -// Value returns the current iterator value at key. -func (i *Iterator) Value(ctx context.Context) ([]byte, error) { - if i.store == nil { - return nil, ErrIteratorClosed - } - return i.store.get(i.state.RLock, ctx, i.key) -} - -// Release will release the store read-lock, and close this iterator. -func (i *Iterator) Release() { - i.state.UnlockMap() - i.state = nil - i.store = nil - i.key = "" - i.entries = nil - i.index = 0 -} diff --git a/vendor/codeberg.org/gruf/go-store/v2/kv/state.go b/vendor/codeberg.org/gruf/go-store/v2/kv/state.go deleted file mode 100644 index 450cd850c..000000000 --- a/vendor/codeberg.org/gruf/go-store/v2/kv/state.go +++ /dev/null @@ -1,116 +0,0 @@ -package kv - -import ( - "context" - "errors" - "io" - - "codeberg.org/gruf/go-mutexes" -) - -// ErrStateClosed is returned on further calls to states after calling Release(). -var ErrStateClosed = errors.New("store/kv: state closed") - -// StateRO provides a read-only window to the store. While this -// state is active during the Read() function window, the entire -// store will be read-locked. The state is thread-safe for concurrent -// use UNTIL the moment that your supplied function to Read() returns. -type StateRO struct { - store *KVStore - state *mutexes.LockState -} - -// Get: see KVStore.Get(). Returns error if state already closed. -func (st *StateRO) Get(ctx context.Context, key string) ([]byte, error) { - if st.store == nil { - return nil, ErrStateClosed - } - return st.store.get(st.state.RLock, ctx, key) -} - -// GetStream: see KVStore.GetStream(). Returns error if state already closed. -func (st *StateRO) GetStream(ctx context.Context, key string) (io.ReadCloser, error) { - if st.store == nil { - return nil, ErrStateClosed - } - return st.store.getStream(st.state.RLock, ctx, key) -} - -// Has: see KVStore.Has(). Returns error if state already closed. -func (st *StateRO) Has(ctx context.Context, key string) (bool, error) { - if st.store == nil { - return false, ErrStateClosed - } - return st.store.has(st.state.RLock, ctx, key) -} - -// Release will release the store read-lock, and close this state. -func (st *StateRO) Release() { - st.state.UnlockMap() - st.state = nil - st.store = nil -} - -// StateRW provides a read-write window to the store. While this -// state is active during the Update() function window, the entire -// store will be locked. The state is thread-safe for concurrent -// use UNTIL the moment that your supplied function to Update() returns. -type StateRW struct { - store *KVStore - state *mutexes.LockState -} - -// Get: see KVStore.Get(). Returns error if state already closed. -func (st *StateRW) Get(ctx context.Context, key string) ([]byte, error) { - if st.store == nil { - return nil, ErrStateClosed - } - return st.store.get(st.state.RLock, ctx, key) -} - -// GetStream: see KVStore.GetStream(). Returns error if state already closed. -func (st *StateRW) GetStream(ctx context.Context, key string) (io.ReadCloser, error) { - if st.store == nil { - return nil, ErrStateClosed - } - return st.store.getStream(st.state.RLock, ctx, key) -} - -// Put: see KVStore.Put(). Returns error if state already closed. -func (st *StateRW) Put(ctx context.Context, key string, value []byte) (int, error) { - if st.store == nil { - return 0, ErrStateClosed - } - return st.store.put(st.state.Lock, ctx, key, value) -} - -// PutStream: see KVStore.PutStream(). Returns error if state already closed. -func (st *StateRW) PutStream(ctx context.Context, key string, r io.Reader) (int64, error) { - if st.store == nil { - return 0, ErrStateClosed - } - return st.store.putStream(st.state.Lock, ctx, key, r) -} - -// Has: see KVStore.Has(). Returns error if state already closed. -func (st *StateRW) Has(ctx context.Context, key string) (bool, error) { - if st.store == nil { - return false, ErrStateClosed - } - return st.store.has(st.state.RLock, ctx, key) -} - -// Delete: see KVStore.Delete(). Returns error if state already closed. -func (st *StateRW) Delete(ctx context.Context, key string) error { - if st.store == nil { - return ErrStateClosed - } - return st.store.delete(st.state.Lock, ctx, key) -} - -// Release will release the store lock, and close this state. -func (st *StateRW) Release() { - st.state.UnlockMap() - st.state = nil - st.store = nil -} diff --git a/vendor/codeberg.org/gruf/go-store/v2/kv/store.go b/vendor/codeberg.org/gruf/go-store/v2/kv/store.go deleted file mode 100644 index 0b878c47f..000000000 --- a/vendor/codeberg.org/gruf/go-store/v2/kv/store.go +++ /dev/null @@ -1,267 +0,0 @@ -package kv - -import ( - "context" - "io" - - "codeberg.org/gruf/go-iotools" - "codeberg.org/gruf/go-mutexes" - "codeberg.org/gruf/go-store/v2/storage" -) - -// KVStore is a very simple, yet performant key-value store -type KVStore struct { - mu mutexes.MutexMap // map of keys to mutexes to protect key access - st storage.Storage // underlying storage implementation -} - -func OpenDisk(path string, cfg *storage.DiskConfig) (*KVStore, error) { - // Attempt to open disk storage - storage, err := storage.OpenDisk(path, cfg) - if err != nil { - return nil, err - } - - // Return new KVStore - return OpenStorage(storage) -} - -func OpenBlock(path string, cfg *storage.BlockConfig) (*KVStore, error) { - // Attempt to open block storage - storage, err := storage.OpenBlock(path, cfg) - if err != nil { - return nil, err - } - - // Return new KVStore - return OpenStorage(storage) -} - -func OpenMemory(overwrites bool) *KVStore { - return New(storage.OpenMemory(100, overwrites)) -} - -func OpenS3(endpoint string, bucket string, cfg *storage.S3Config) (*KVStore, error) { - // Attempt to open S3 storage - storage, err := storage.OpenS3(endpoint, bucket, cfg) - if err != nil { - return nil, err - } - - // Return new KVStore - return OpenStorage(storage) -} - -// OpenStorage will return a new KVStore instance based on Storage, performing an initial storage.Clean(). -func OpenStorage(storage storage.Storage) (*KVStore, error) { - // Perform initial storage clean - err := storage.Clean(context.Background()) - if err != nil { - return nil, err - } - - // Return new KVStore - return New(storage), nil -} - -// New will simply return a new KVStore instance based on Storage. -func New(storage storage.Storage) *KVStore { - if storage == nil { - panic("nil storage") - } - return &KVStore{ - mu: mutexes.NewMap(-1, -1), - st: storage, - } -} - -// RLock acquires a read-lock on supplied key, returning unlock function. -func (st *KVStore) RLock(key string) (runlock func()) { - return st.mu.RLock(key) -} - -// Lock acquires a write-lock on supplied key, returning unlock function. -func (st *KVStore) Lock(key string) (unlock func()) { - return st.mu.Lock(key) -} - -// Get fetches the bytes for supplied key in the store. -func (st *KVStore) Get(ctx context.Context, key string) ([]byte, error) { - return st.get(st.RLock, ctx, key) -} - -// get performs the underlying logic for KVStore.Get(), using supplied read lock func to allow use with states. -func (st *KVStore) get(rlock func(string) func(), ctx context.Context, key string) ([]byte, error) { - // Acquire read lock for key - runlock := rlock(key) - defer runlock() - - // Read file bytes from storage - return st.st.ReadBytes(ctx, key) -} - -// GetStream fetches a ReadCloser for the bytes at the supplied key in the store. -func (st *KVStore) GetStream(ctx context.Context, key string) (io.ReadCloser, error) { - return st.getStream(st.RLock, ctx, key) -} - -// getStream performs the underlying logic for KVStore.GetStream(), using supplied read lock func to allow use with states. -func (st *KVStore) getStream(rlock func(string) func(), ctx context.Context, key string) (io.ReadCloser, error) { - // Acquire read lock for key - runlock := rlock(key) - - // Attempt to open stream for read - rd, err := st.st.ReadStream(ctx, key) - if err != nil { - runlock() - return nil, err - } - - var unlocked bool - - // Wrap readcloser to call our own callback - return iotools.ReadCloser(rd, iotools.CloserFunc(func() error { - if !unlocked { - unlocked = true - defer runlock() - } - return rd.Close() - })), nil -} - -// Put places the bytes at the supplied key in the store. -func (st *KVStore) Put(ctx context.Context, key string, value []byte) (int, error) { - return st.put(st.Lock, ctx, key, value) -} - -// put performs the underlying logic for KVStore.Put(), using supplied lock func to allow use with states. -func (st *KVStore) put(lock func(string) func(), ctx context.Context, key string, value []byte) (int, error) { - // Acquire write lock for key - unlock := lock(key) - defer unlock() - - // Write file bytes to storage - return st.st.WriteBytes(ctx, key, value) -} - -// PutStream writes the bytes from the supplied Reader at the supplied key in the store. -func (st *KVStore) PutStream(ctx context.Context, key string, r io.Reader) (int64, error) { - return st.putStream(st.Lock, ctx, key, r) -} - -// putStream performs the underlying logic for KVStore.PutStream(), using supplied lock func to allow use with states. -func (st *KVStore) putStream(lock func(string) func(), ctx context.Context, key string, r io.Reader) (int64, error) { - // Acquire write lock for key - unlock := lock(key) - defer unlock() - - // Write file stream to storage - return st.st.WriteStream(ctx, key, r) -} - -// Has checks whether the supplied key exists in the store. -func (st *KVStore) Has(ctx context.Context, key string) (bool, error) { - return st.has(st.RLock, ctx, key) -} - -// has performs the underlying logic for KVStore.Has(), using supplied read lock func to allow use with states. -func (st *KVStore) has(rlock func(string) func(), ctx context.Context, key string) (bool, error) { - // Acquire read lock for key - runlock := rlock(key) - defer runlock() - - // Stat file in storage - return st.st.Stat(ctx, key) -} - -// Delete removes value at supplied key from the store. -func (st *KVStore) Delete(ctx context.Context, key string) error { - return st.delete(st.Lock, ctx, key) -} - -// delete performs the underlying logic for KVStore.Delete(), using supplied lock func to allow use with states. -func (st *KVStore) delete(lock func(string) func(), ctx context.Context, key string) error { - // Acquire write lock for key - unlock := lock(key) - defer unlock() - - // Remove file from storage - return st.st.Remove(ctx, key) -} - -// Iterator returns an Iterator for key-value pairs in the store, using supplied match function -func (st *KVStore) Iterator(ctx context.Context, matchFn func(string) bool) (*Iterator, error) { - if matchFn == nil { - // By default simply match all keys - matchFn = func(string) bool { return true } - } - - // Get store read lock state - state := st.mu.RLockMap() - - var entries []storage.Entry - - walkFn := func(ctx context.Context, entry storage.Entry) error { - // Ignore unmatched entries - if !matchFn(entry.Key) { - return nil - } - - // Add to entries - entries = append(entries, entry) - return nil - } - - // Collate keys in storage with our walk function - err := st.st.WalkKeys(ctx, storage.WalkKeysOptions{WalkFn: walkFn}) - if err != nil { - state.UnlockMap() - return nil, err - } - - // Return new iterator - return &Iterator{ - store: st, - state: state, - entries: entries, - index: -1, - key: "", - }, nil -} - -// Read provides a read-only window to the store, holding it in a read-locked state until release. -func (st *KVStore) Read() *StateRO { - state := st.mu.RLockMap() - return &StateRO{store: st, state: state} -} - -// ReadFn provides a read-only window to the store, holding it in a read-locked state until fn return.. -func (st *KVStore) ReadFn(fn func(*StateRO)) { - // Acquire read-only state - state := st.Read() - defer state.Release() - - // Pass to fn - fn(state) -} - -// Update provides a read-write window to the store, holding it in a write-locked state until release. -func (st *KVStore) Update() *StateRW { - state := st.mu.LockMap() - return &StateRW{store: st, state: state} -} - -// UpdateFn provides a read-write window to the store, holding it in a write-locked state until fn return. -func (st *KVStore) UpdateFn(fn func(*StateRW)) { - // Acquire read-write state - state := st.Update() - defer state.Release() - - // Pass to fn - fn(state) -} - -// Close will close the underlying storage, the mutex map locking (e.g. RLock(), Lock()) will continue to function. -func (st *KVStore) Close() error { - return st.st.Close() -} diff --git a/vendor/codeberg.org/gruf/go-store/v2/storage/block.go b/vendor/codeberg.org/gruf/go-store/v2/storage/block.archived similarity index 100% rename from vendor/codeberg.org/gruf/go-store/v2/storage/block.go rename to vendor/codeberg.org/gruf/go-store/v2/storage/block.archived diff --git a/vendor/codeberg.org/gruf/go-store/v2/storage/block_test.archived b/vendor/codeberg.org/gruf/go-store/v2/storage/block_test.archived new file mode 100644 index 000000000..8436f067f --- /dev/null +++ b/vendor/codeberg.org/gruf/go-store/v2/storage/block_test.archived @@ -0,0 +1,38 @@ +package storage_test + +import ( + "os" + "testing" + + "codeberg.org/gruf/go-store/v2/storage" +) + +func TestBlockStorage(t *testing.T) { + // Set test path, defer deleting it + testPath := "blockstorage.test" + t.Cleanup(func() { + os.RemoveAll(testPath) + }) + + // Open new blockstorage instance + st, err := storage.OpenBlock(testPath, nil) + if err != nil { + t.Fatalf("Failed opening storage: %v", err) + } + + // Attempt multi open of same instance + _, err = storage.OpenBlock(testPath, nil) + if err == nil { + t.Fatal("Successfully opened a locked storage instance") + } + + // Run the storage tests + testStorage(t, st) + + // Test reopen storage path + st, err = storage.OpenBlock(testPath, nil) + if err != nil { + t.Fatalf("Failed opening storage: %v", err) + } + st.Close() +} diff --git a/vendor/github.com/klauspost/compress/flate/deflate.go b/vendor/github.com/klauspost/compress/flate/deflate.go index 5faea0b2b..de912e187 100644 --- a/vendor/github.com/klauspost/compress/flate/deflate.go +++ b/vendor/github.com/klauspost/compress/flate/deflate.go @@ -7,6 +7,7 @@ package flate import ( "encoding/binary" + "errors" "fmt" "io" "math" @@ -833,6 +834,12 @@ func (d *compressor) init(w io.Writer, level int) (err error) { d.initDeflate() d.fill = (*compressor).fillDeflate d.step = (*compressor).deflateLazy + case -level >= MinCustomWindowSize && -level <= MaxCustomWindowSize: + d.w.logNewTablePenalty = 7 + d.fast = &fastEncL5Window{maxOffset: int32(-level), cur: maxStoreBlockSize} + d.window = make([]byte, maxStoreBlockSize) + d.fill = (*compressor).fillBlock + d.step = (*compressor).storeFast default: return fmt.Errorf("flate: invalid compression level %d: want value in range [-2, 9]", level) } @@ -929,6 +936,28 @@ func NewWriterDict(w io.Writer, level int, dict []byte) (*Writer, error) { return zw, err } +// MinCustomWindowSize is the minimum window size that can be sent to NewWriterWindow. +const MinCustomWindowSize = 32 + +// MaxCustomWindowSize is the maximum custom window that can be sent to NewWriterWindow. +const MaxCustomWindowSize = windowSize + +// NewWriterWindow returns a new Writer compressing data with a custom window size. +// windowSize must be from MinCustomWindowSize to MaxCustomWindowSize. +func NewWriterWindow(w io.Writer, windowSize int) (*Writer, error) { + if windowSize < MinCustomWindowSize { + return nil, errors.New("flate: requested window size less than MinWindowSize") + } + if windowSize > MaxCustomWindowSize { + return nil, errors.New("flate: requested window size bigger than MaxCustomWindowSize") + } + var dw Writer + if err := dw.d.init(w, -windowSize); err != nil { + return nil, err + } + return &dw, nil +} + // A Writer takes data written to it and writes the compressed // form of that data to an underlying writer (see NewWriter). type Writer struct { diff --git a/vendor/github.com/klauspost/compress/flate/fast_encoder.go b/vendor/github.com/klauspost/compress/flate/fast_encoder.go index 24caf5f70..c8124b5c4 100644 --- a/vendor/github.com/klauspost/compress/flate/fast_encoder.go +++ b/vendor/github.com/klauspost/compress/flate/fast_encoder.go @@ -8,7 +8,6 @@ package flate import ( "encoding/binary" "fmt" - "math/bits" ) type fastEnc interface { @@ -192,25 +191,3 @@ func (e *fastGen) Reset() { } e.hist = e.hist[:0] } - -// matchLen returns the maximum length. -// 'a' must be the shortest of the two. -func matchLen(a, b []byte) int { - var checked int - - for len(a) >= 8 { - if diff := binary.LittleEndian.Uint64(a) ^ binary.LittleEndian.Uint64(b); diff != 0 { - return checked + (bits.TrailingZeros64(diff) >> 3) - } - checked += 8 - a = a[8:] - b = b[8:] - } - b = b[:len(a)] - for i := range a { - if a[i] != b[i] { - return i + checked - } - } - return len(a) + checked -} diff --git a/vendor/github.com/klauspost/compress/flate/inflate.go b/vendor/github.com/klauspost/compress/flate/inflate.go index 414c0bea9..2f410d64f 100644 --- a/vendor/github.com/klauspost/compress/flate/inflate.go +++ b/vendor/github.com/klauspost/compress/flate/inflate.go @@ -120,8 +120,9 @@ func (h *huffmanDecoder) init(lengths []int) bool { const sanity = false if h.chunks == nil { - h.chunks = &[huffmanNumChunks]uint16{} + h.chunks = new([huffmanNumChunks]uint16) } + if h.maxRead != 0 { *h = huffmanDecoder{chunks: h.chunks, links: h.links} } @@ -175,6 +176,7 @@ func (h *huffmanDecoder) init(lengths []int) bool { } h.maxRead = min + chunks := h.chunks[:] for i := range chunks { chunks[i] = 0 @@ -202,8 +204,7 @@ func (h *huffmanDecoder) init(lengths []int) bool { if cap(h.links[off]) < numLinks { h.links[off] = make([]uint16, numLinks) } else { - links := h.links[off][:0] - h.links[off] = links[:numLinks] + h.links[off] = h.links[off][:numLinks] } } } else { @@ -277,7 +278,7 @@ func (h *huffmanDecoder) init(lengths []int) bool { return true } -// The actual read interface needed by NewReader. +// Reader is the actual read interface needed by NewReader. // If the passed in io.Reader does not also have ReadByte, // the NewReader will introduce its own buffering. type Reader interface { @@ -285,6 +286,18 @@ type Reader interface { io.ByteReader } +type step uint8 + +const ( + copyData step = iota + 1 + nextBlock + huffmanBytesBuffer + huffmanBytesReader + huffmanBufioReader + huffmanStringsReader + huffmanGenericReader +) + // Decompress state. type decompressor struct { // Input source. @@ -303,7 +316,7 @@ type decompressor struct { // Next step in the decompression, // and decompression state. - step func(*decompressor) + step step stepState int err error toRead []byte @@ -342,7 +355,7 @@ func (f *decompressor) nextBlock() { // compressed, fixed Huffman tables f.hl = &fixedHuffmanDecoder f.hd = nil - f.huffmanBlockDecoder()() + f.huffmanBlockDecoder() if debugDecode { fmt.Println("predefinied huffman block") } @@ -353,7 +366,7 @@ func (f *decompressor) nextBlock() { } f.hl = &f.h1 f.hd = &f.h2 - f.huffmanBlockDecoder()() + f.huffmanBlockDecoder() if debugDecode { fmt.Println("dynamic huffman block") } @@ -379,14 +392,16 @@ func (f *decompressor) Read(b []byte) (int, error) { if f.err != nil { return 0, f.err } - f.step(f) + + f.doStep() + if f.err != nil && len(f.toRead) == 0 { f.toRead = f.dict.readFlush() // Flush what's left in case of error } } } -// Support the io.WriteTo interface for io.Copy and friends. +// WriteTo implements the io.WriteTo interface for io.Copy and friends. func (f *decompressor) WriteTo(w io.Writer) (int64, error) { total := int64(0) flushed := false @@ -410,7 +425,7 @@ func (f *decompressor) WriteTo(w io.Writer) (int64, error) { return total, f.err } if f.err == nil { - f.step(f) + f.doStep() } if len(f.toRead) == 0 && f.err != nil && !flushed { f.toRead = f.dict.readFlush() // Flush what's left in case of error @@ -631,7 +646,7 @@ func (f *decompressor) copyData() { if f.dict.availWrite() == 0 || f.copyLen > 0 { f.toRead = f.dict.readFlush() - f.step = (*decompressor).copyData + f.step = copyData return } f.finishBlock() @@ -644,7 +659,28 @@ func (f *decompressor) finishBlock() { } f.err = io.EOF } - f.step = (*decompressor).nextBlock + f.step = nextBlock +} + +func (f *decompressor) doStep() { + switch f.step { + case copyData: + f.copyData() + case nextBlock: + f.nextBlock() + case huffmanBytesBuffer: + f.huffmanBytesBuffer() + case huffmanBytesReader: + f.huffmanBytesReader() + case huffmanBufioReader: + f.huffmanBufioReader() + case huffmanStringsReader: + f.huffmanStringsReader() + case huffmanGenericReader: + f.huffmanGenericReader() + default: + panic("BUG: unexpected step state") + } } // noEOF returns err, unless err == io.EOF, in which case it returns io.ErrUnexpectedEOF. @@ -747,7 +783,7 @@ func (f *decompressor) Reset(r io.Reader, dict []byte) error { h1: f.h1, h2: f.h2, dict: f.dict, - step: (*decompressor).nextBlock, + step: nextBlock, } f.dict.init(maxMatchOffset, dict) return nil @@ -768,7 +804,7 @@ func NewReader(r io.Reader) io.ReadCloser { f.r = makeReader(r) f.bits = new([maxNumLit + maxNumDist]int) f.codebits = new([numCodes]int) - f.step = (*decompressor).nextBlock + f.step = nextBlock f.dict.init(maxMatchOffset, nil) return &f } @@ -787,7 +823,7 @@ func NewReaderDict(r io.Reader, dict []byte) io.ReadCloser { f.r = makeReader(r) f.bits = new([maxNumLit + maxNumDist]int) f.codebits = new([numCodes]int) - f.step = (*decompressor).nextBlock + f.step = nextBlock f.dict.init(maxMatchOffset, dict) return &f } diff --git a/vendor/github.com/klauspost/compress/flate/inflate_gen.go b/vendor/github.com/klauspost/compress/flate/inflate_gen.go index 61342b6b8..2b2f993f7 100644 --- a/vendor/github.com/klauspost/compress/flate/inflate_gen.go +++ b/vendor/github.com/klauspost/compress/flate/inflate_gen.go @@ -85,7 +85,7 @@ readLiteral: dict.writeByte(byte(v)) if dict.availWrite() == 0 { f.toRead = dict.readFlush() - f.step = (*decompressor).huffmanBytesBuffer + f.step = huffmanBytesBuffer f.stepState = stateInit f.b, f.nb = fb, fnb return @@ -251,7 +251,7 @@ copyHistory: if dict.availWrite() == 0 || f.copyLen > 0 { f.toRead = dict.readFlush() - f.step = (*decompressor).huffmanBytesBuffer // We need to continue this work + f.step = huffmanBytesBuffer // We need to continue this work f.stepState = stateDict f.b, f.nb = fb, fnb return @@ -336,7 +336,7 @@ readLiteral: dict.writeByte(byte(v)) if dict.availWrite() == 0 { f.toRead = dict.readFlush() - f.step = (*decompressor).huffmanBytesReader + f.step = huffmanBytesReader f.stepState = stateInit f.b, f.nb = fb, fnb return @@ -502,7 +502,7 @@ copyHistory: if dict.availWrite() == 0 || f.copyLen > 0 { f.toRead = dict.readFlush() - f.step = (*decompressor).huffmanBytesReader // We need to continue this work + f.step = huffmanBytesReader // We need to continue this work f.stepState = stateDict f.b, f.nb = fb, fnb return @@ -587,7 +587,7 @@ readLiteral: dict.writeByte(byte(v)) if dict.availWrite() == 0 { f.toRead = dict.readFlush() - f.step = (*decompressor).huffmanBufioReader + f.step = huffmanBufioReader f.stepState = stateInit f.b, f.nb = fb, fnb return @@ -753,7 +753,7 @@ copyHistory: if dict.availWrite() == 0 || f.copyLen > 0 { f.toRead = dict.readFlush() - f.step = (*decompressor).huffmanBufioReader // We need to continue this work + f.step = huffmanBufioReader // We need to continue this work f.stepState = stateDict f.b, f.nb = fb, fnb return @@ -838,7 +838,7 @@ readLiteral: dict.writeByte(byte(v)) if dict.availWrite() == 0 { f.toRead = dict.readFlush() - f.step = (*decompressor).huffmanStringsReader + f.step = huffmanStringsReader f.stepState = stateInit f.b, f.nb = fb, fnb return @@ -1004,7 +1004,7 @@ copyHistory: if dict.availWrite() == 0 || f.copyLen > 0 { f.toRead = dict.readFlush() - f.step = (*decompressor).huffmanStringsReader // We need to continue this work + f.step = huffmanStringsReader // We need to continue this work f.stepState = stateDict f.b, f.nb = fb, fnb return @@ -1089,7 +1089,7 @@ readLiteral: dict.writeByte(byte(v)) if dict.availWrite() == 0 { f.toRead = dict.readFlush() - f.step = (*decompressor).huffmanGenericReader + f.step = huffmanGenericReader f.stepState = stateInit f.b, f.nb = fb, fnb return @@ -1255,7 +1255,7 @@ copyHistory: if dict.availWrite() == 0 || f.copyLen > 0 { f.toRead = dict.readFlush() - f.step = (*decompressor).huffmanGenericReader // We need to continue this work + f.step = huffmanGenericReader // We need to continue this work f.stepState = stateDict f.b, f.nb = fb, fnb return @@ -1265,19 +1265,19 @@ copyHistory: // Not reached } -func (f *decompressor) huffmanBlockDecoder() func() { +func (f *decompressor) huffmanBlockDecoder() { switch f.r.(type) { case *bytes.Buffer: - return f.huffmanBytesBuffer + f.huffmanBytesBuffer() case *bytes.Reader: - return f.huffmanBytesReader + f.huffmanBytesReader() case *bufio.Reader: - return f.huffmanBufioReader + f.huffmanBufioReader() case *strings.Reader: - return f.huffmanStringsReader + f.huffmanStringsReader() case Reader: - return f.huffmanGenericReader + f.huffmanGenericReader() default: - return f.huffmanGenericReader + f.huffmanGenericReader() } } diff --git a/vendor/github.com/klauspost/compress/flate/level5.go b/vendor/github.com/klauspost/compress/flate/level5.go index 83ef50ba4..1f61ec182 100644 --- a/vendor/github.com/klauspost/compress/flate/level5.go +++ b/vendor/github.com/klauspost/compress/flate/level5.go @@ -308,3 +308,401 @@ emitRemainder: emitLiteral(dst, src[nextEmit:]) } } + +// fastEncL5Window is a level 5 encoder, +// but with a custom window size. +type fastEncL5Window struct { + hist []byte + cur int32 + maxOffset int32 + table [tableSize]tableEntry + bTable [tableSize]tableEntryPrev +} + +func (e *fastEncL5Window) Encode(dst *tokens, src []byte) { + const ( + inputMargin = 12 - 1 + minNonLiteralBlockSize = 1 + 1 + inputMargin + hashShortBytes = 4 + ) + maxMatchOffset := e.maxOffset + if debugDeflate && e.cur < 0 { + panic(fmt.Sprint("e.cur < 0: ", e.cur)) + } + + // Protect against e.cur wraparound. + for e.cur >= bufferReset { + if len(e.hist) == 0 { + for i := range e.table[:] { + e.table[i] = tableEntry{} + } + for i := range e.bTable[:] { + e.bTable[i] = tableEntryPrev{} + } + e.cur = maxMatchOffset + break + } + // Shift down everything in the table that isn't already too far away. + minOff := e.cur + int32(len(e.hist)) - maxMatchOffset + for i := range e.table[:] { + v := e.table[i].offset + if v <= minOff { + v = 0 + } else { + v = v - e.cur + maxMatchOffset + } + e.table[i].offset = v + } + for i := range e.bTable[:] { + v := e.bTable[i] + if v.Cur.offset <= minOff { + v.Cur.offset = 0 + v.Prev.offset = 0 + } else { + v.Cur.offset = v.Cur.offset - e.cur + maxMatchOffset + if v.Prev.offset <= minOff { + v.Prev.offset = 0 + } else { + v.Prev.offset = v.Prev.offset - e.cur + maxMatchOffset + } + } + e.bTable[i] = v + } + e.cur = maxMatchOffset + } + + s := e.addBlock(src) + + // This check isn't in the Snappy implementation, but there, the caller + // instead of the callee handles this case. + if len(src) < minNonLiteralBlockSize { + // We do not fill the token table. + // This will be picked up by caller. + dst.n = uint16(len(src)) + return + } + + // Override src + src = e.hist + nextEmit := s + + // sLimit is when to stop looking for offset/length copies. The inputMargin + // lets us use a fast path for emitLiteral in the main loop, while we are + // looking for copies. + sLimit := int32(len(src) - inputMargin) + + // nextEmit is where in src the next emitLiteral should start from. + cv := load6432(src, s) + for { + const skipLog = 6 + const doEvery = 1 + + nextS := s + var l int32 + var t int32 + for { + nextHashS := hashLen(cv, tableBits, hashShortBytes) + nextHashL := hash7(cv, tableBits) + + s = nextS + nextS = s + doEvery + (s-nextEmit)>>skipLog + if nextS > sLimit { + goto emitRemainder + } + // Fetch a short+long candidate + sCandidate := e.table[nextHashS] + lCandidate := e.bTable[nextHashL] + next := load6432(src, nextS) + entry := tableEntry{offset: s + e.cur} + e.table[nextHashS] = entry + eLong := &e.bTable[nextHashL] + eLong.Cur, eLong.Prev = entry, eLong.Cur + + nextHashS = hashLen(next, tableBits, hashShortBytes) + nextHashL = hash7(next, tableBits) + + t = lCandidate.Cur.offset - e.cur + if s-t < maxMatchOffset { + if uint32(cv) == load3232(src, lCandidate.Cur.offset-e.cur) { + // Store the next match + e.table[nextHashS] = tableEntry{offset: nextS + e.cur} + eLong := &e.bTable[nextHashL] + eLong.Cur, eLong.Prev = tableEntry{offset: nextS + e.cur}, eLong.Cur + + t2 := lCandidate.Prev.offset - e.cur + if s-t2 < maxMatchOffset && uint32(cv) == load3232(src, lCandidate.Prev.offset-e.cur) { + l = e.matchlen(s+4, t+4, src) + 4 + ml1 := e.matchlen(s+4, t2+4, src) + 4 + if ml1 > l { + t = t2 + l = ml1 + break + } + } + break + } + t = lCandidate.Prev.offset - e.cur + if s-t < maxMatchOffset && uint32(cv) == load3232(src, lCandidate.Prev.offset-e.cur) { + // Store the next match + e.table[nextHashS] = tableEntry{offset: nextS + e.cur} + eLong := &e.bTable[nextHashL] + eLong.Cur, eLong.Prev = tableEntry{offset: nextS + e.cur}, eLong.Cur + break + } + } + + t = sCandidate.offset - e.cur + if s-t < maxMatchOffset && uint32(cv) == load3232(src, sCandidate.offset-e.cur) { + // Found a 4 match... + l = e.matchlen(s+4, t+4, src) + 4 + lCandidate = e.bTable[nextHashL] + // Store the next match + + e.table[nextHashS] = tableEntry{offset: nextS + e.cur} + eLong := &e.bTable[nextHashL] + eLong.Cur, eLong.Prev = tableEntry{offset: nextS + e.cur}, eLong.Cur + + // If the next long is a candidate, use that... + t2 := lCandidate.Cur.offset - e.cur + if nextS-t2 < maxMatchOffset { + if load3232(src, lCandidate.Cur.offset-e.cur) == uint32(next) { + ml := e.matchlen(nextS+4, t2+4, src) + 4 + if ml > l { + t = t2 + s = nextS + l = ml + break + } + } + // If the previous long is a candidate, use that... + t2 = lCandidate.Prev.offset - e.cur + if nextS-t2 < maxMatchOffset && load3232(src, lCandidate.Prev.offset-e.cur) == uint32(next) { + ml := e.matchlen(nextS+4, t2+4, src) + 4 + if ml > l { + t = t2 + s = nextS + l = ml + break + } + } + } + break + } + cv = next + } + + // A 4-byte match has been found. We'll later see if more than 4 bytes + // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit + // them as literal bytes. + + if l == 0 { + // Extend the 4-byte match as long as possible. + l = e.matchlenLong(s+4, t+4, src) + 4 + } else if l == maxMatchLength { + l += e.matchlenLong(s+l, t+l, src) + } + + // Try to locate a better match by checking the end of best match... + if sAt := s + l; l < 30 && sAt < sLimit { + // Allow some bytes at the beginning to mismatch. + // Sweet spot is 2/3 bytes depending on input. + // 3 is only a little better when it is but sometimes a lot worse. + // The skipped bytes are tested in Extend backwards, + // and still picked up as part of the match if they do. + const skipBeginning = 2 + eLong := e.bTable[hash7(load6432(src, sAt), tableBits)].Cur.offset + t2 := eLong - e.cur - l + skipBeginning + s2 := s + skipBeginning + off := s2 - t2 + if t2 >= 0 && off < maxMatchOffset && off > 0 { + if l2 := e.matchlenLong(s2, t2, src); l2 > l { + t = t2 + l = l2 + s = s2 + } + } + } + + // Extend backwards + for t > 0 && s > nextEmit && src[t-1] == src[s-1] { + s-- + t-- + l++ + } + if nextEmit < s { + if false { + emitLiteral(dst, src[nextEmit:s]) + } else { + for _, v := range src[nextEmit:s] { + dst.tokens[dst.n] = token(v) + dst.litHist[v]++ + dst.n++ + } + } + } + if debugDeflate { + if t >= s { + panic(fmt.Sprintln("s-t", s, t)) + } + if (s - t) > maxMatchOffset { + panic(fmt.Sprintln("mmo", s-t)) + } + if l < baseMatchLength { + panic("bml") + } + } + + dst.AddMatchLong(l, uint32(s-t-baseMatchOffset)) + s += l + nextEmit = s + if nextS >= s { + s = nextS + 1 + } + + if s >= sLimit { + goto emitRemainder + } + + // Store every 3rd hash in-between. + if true { + const hashEvery = 3 + i := s - l + 1 + if i < s-1 { + cv := load6432(src, i) + t := tableEntry{offset: i + e.cur} + e.table[hashLen(cv, tableBits, hashShortBytes)] = t + eLong := &e.bTable[hash7(cv, tableBits)] + eLong.Cur, eLong.Prev = t, eLong.Cur + + // Do an long at i+1 + cv >>= 8 + t = tableEntry{offset: t.offset + 1} + eLong = &e.bTable[hash7(cv, tableBits)] + eLong.Cur, eLong.Prev = t, eLong.Cur + + // We only have enough bits for a short entry at i+2 + cv >>= 8 + t = tableEntry{offset: t.offset + 1} + e.table[hashLen(cv, tableBits, hashShortBytes)] = t + + // Skip one - otherwise we risk hitting 's' + i += 4 + for ; i < s-1; i += hashEvery { + cv := load6432(src, i) + t := tableEntry{offset: i + e.cur} + t2 := tableEntry{offset: t.offset + 1} + eLong := &e.bTable[hash7(cv, tableBits)] + eLong.Cur, eLong.Prev = t, eLong.Cur + e.table[hashLen(cv>>8, tableBits, hashShortBytes)] = t2 + } + } + } + + // We could immediately start working at s now, but to improve + // compression we first update the hash table at s-1 and at s. + x := load6432(src, s-1) + o := e.cur + s - 1 + prevHashS := hashLen(x, tableBits, hashShortBytes) + prevHashL := hash7(x, tableBits) + e.table[prevHashS] = tableEntry{offset: o} + eLong := &e.bTable[prevHashL] + eLong.Cur, eLong.Prev = tableEntry{offset: o}, eLong.Cur + cv = x >> 8 + } + +emitRemainder: + if int(nextEmit) < len(src) { + // If nothing was added, don't encode literals. + if dst.n == 0 { + return + } + + emitLiteral(dst, src[nextEmit:]) + } +} + +// Reset the encoding table. +func (e *fastEncL5Window) Reset() { + // We keep the same allocs, since we are compressing the same block sizes. + if cap(e.hist) < allocHistory { + e.hist = make([]byte, 0, allocHistory) + } + + // We offset current position so everything will be out of reach. + // If we are above the buffer reset it will be cleared anyway since len(hist) == 0. + if e.cur <= int32(bufferReset) { + e.cur += e.maxOffset + int32(len(e.hist)) + } + e.hist = e.hist[:0] +} + +func (e *fastEncL5Window) addBlock(src []byte) int32 { + // check if we have space already + maxMatchOffset := e.maxOffset + + if len(e.hist)+len(src) > cap(e.hist) { + if cap(e.hist) == 0 { + e.hist = make([]byte, 0, allocHistory) + } else { + if cap(e.hist) < int(maxMatchOffset*2) { + panic("unexpected buffer size") + } + // Move down + offset := int32(len(e.hist)) - maxMatchOffset + copy(e.hist[0:maxMatchOffset], e.hist[offset:]) + e.cur += offset + e.hist = e.hist[:maxMatchOffset] + } + } + s := int32(len(e.hist)) + e.hist = append(e.hist, src...) + return s +} + +// matchlen will return the match length between offsets and t in src. +// The maximum length returned is maxMatchLength - 4. +// It is assumed that s > t, that t >=0 and s < len(src). +func (e *fastEncL5Window) matchlen(s, t int32, src []byte) int32 { + if debugDecode { + if t >= s { + panic(fmt.Sprint("t >=s:", t, s)) + } + if int(s) >= len(src) { + panic(fmt.Sprint("s >= len(src):", s, len(src))) + } + if t < 0 { + panic(fmt.Sprint("t < 0:", t)) + } + if s-t > e.maxOffset { + panic(fmt.Sprint(s, "-", t, "(", s-t, ") > maxMatchLength (", maxMatchOffset, ")")) + } + } + s1 := int(s) + maxMatchLength - 4 + if s1 > len(src) { + s1 = len(src) + } + + // Extend the match to be as long as possible. + return int32(matchLen(src[s:s1], src[t:])) +} + +// matchlenLong will return the match length between offsets and t in src. +// It is assumed that s > t, that t >=0 and s < len(src). +func (e *fastEncL5Window) matchlenLong(s, t int32, src []byte) int32 { + if debugDeflate { + if t >= s { + panic(fmt.Sprint("t >=s:", t, s)) + } + if int(s) >= len(src) { + panic(fmt.Sprint("s >= len(src):", s, len(src))) + } + if t < 0 { + panic(fmt.Sprint("t < 0:", t)) + } + if s-t > e.maxOffset { + panic(fmt.Sprint(s, "-", t, "(", s-t, ") > maxMatchLength (", maxMatchOffset, ")")) + } + } + // Extend the match to be as long as possible. + return int32(matchLen(src[s:], src[t:])) +} diff --git a/vendor/github.com/klauspost/compress/flate/matchlen_amd64.go b/vendor/github.com/klauspost/compress/flate/matchlen_amd64.go new file mode 100644 index 000000000..4bd388584 --- /dev/null +++ b/vendor/github.com/klauspost/compress/flate/matchlen_amd64.go @@ -0,0 +1,16 @@ +//go:build amd64 && !appengine && !noasm && gc +// +build amd64,!appengine,!noasm,gc + +// Copyright 2019+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. + +package flate + +// matchLen returns how many bytes match in a and b +// +// It assumes that: +// +// len(a) <= len(b) and len(a) > 0 +// +//go:noescape +func matchLen(a []byte, b []byte) int diff --git a/vendor/github.com/klauspost/compress/flate/matchlen_amd64.s b/vendor/github.com/klauspost/compress/flate/matchlen_amd64.s new file mode 100644 index 000000000..9a7655c0f --- /dev/null +++ b/vendor/github.com/klauspost/compress/flate/matchlen_amd64.s @@ -0,0 +1,68 @@ +// Copied from S2 implementation. + +//go:build !appengine && !noasm && gc && !noasm + +#include "textflag.h" + +// func matchLen(a []byte, b []byte) int +// Requires: BMI +TEXT ·matchLen(SB), NOSPLIT, $0-56 + MOVQ a_base+0(FP), AX + MOVQ b_base+24(FP), CX + MOVQ a_len+8(FP), DX + + // matchLen + XORL SI, SI + CMPL DX, $0x08 + JB matchlen_match4_standalone + +matchlen_loopback_standalone: + MOVQ (AX)(SI*1), BX + XORQ (CX)(SI*1), BX + TESTQ BX, BX + JZ matchlen_loop_standalone + +#ifdef GOAMD64_v3 + TZCNTQ BX, BX +#else + BSFQ BX, BX +#endif + SARQ $0x03, BX + LEAL (SI)(BX*1), SI + JMP gen_match_len_end + +matchlen_loop_standalone: + LEAL -8(DX), DX + LEAL 8(SI), SI + CMPL DX, $0x08 + JAE matchlen_loopback_standalone + +matchlen_match4_standalone: + CMPL DX, $0x04 + JB matchlen_match2_standalone + MOVL (AX)(SI*1), BX + CMPL (CX)(SI*1), BX + JNE matchlen_match2_standalone + LEAL -4(DX), DX + LEAL 4(SI), SI + +matchlen_match2_standalone: + CMPL DX, $0x02 + JB matchlen_match1_standalone + MOVW (AX)(SI*1), BX + CMPW (CX)(SI*1), BX + JNE matchlen_match1_standalone + LEAL -2(DX), DX + LEAL 2(SI), SI + +matchlen_match1_standalone: + CMPL DX, $0x01 + JB gen_match_len_end + MOVB (AX)(SI*1), BL + CMPB (CX)(SI*1), BL + JNE gen_match_len_end + INCL SI + +gen_match_len_end: + MOVQ SI, ret+48(FP) + RET diff --git a/vendor/github.com/klauspost/compress/flate/matchlen_generic.go b/vendor/github.com/klauspost/compress/flate/matchlen_generic.go new file mode 100644 index 000000000..ad5cd814b --- /dev/null +++ b/vendor/github.com/klauspost/compress/flate/matchlen_generic.go @@ -0,0 +1,33 @@ +//go:build !amd64 || appengine || !gc || noasm +// +build !amd64 appengine !gc noasm + +// Copyright 2019+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. + +package flate + +import ( + "encoding/binary" + "math/bits" +) + +// matchLen returns the maximum common prefix length of a and b. +// a must be the shortest of the two. +func matchLen(a, b []byte) (n int) { + for ; len(a) >= 8 && len(b) >= 8; a, b = a[8:], b[8:] { + diff := binary.LittleEndian.Uint64(a) ^ binary.LittleEndian.Uint64(b) + if diff != 0 { + return n + bits.TrailingZeros64(diff)>>3 + } + n += 8 + } + + for i := range a { + if a[i] != b[i] { + break + } + n++ + } + return n + +} diff --git a/vendor/github.com/klauspost/compress/gzip/gunzip.go b/vendor/github.com/klauspost/compress/gzip/gunzip.go index 6d630c390..dc2362a63 100644 --- a/vendor/github.com/klauspost/compress/gzip/gunzip.go +++ b/vendor/github.com/klauspost/compress/gzip/gunzip.go @@ -106,6 +106,7 @@ func (z *Reader) Reset(r io.Reader) error { *z = Reader{ decompressor: z.decompressor, multistream: true, + br: z.br, } if rr, ok := r.(flate.Reader); ok { z.r = rr diff --git a/vendor/github.com/klauspost/compress/gzip/gzip.go b/vendor/github.com/klauspost/compress/gzip/gzip.go index 26203851b..5bc720593 100644 --- a/vendor/github.com/klauspost/compress/gzip/gzip.go +++ b/vendor/github.com/klauspost/compress/gzip/gzip.go @@ -74,6 +74,27 @@ func NewWriterLevel(w io.Writer, level int) (*Writer, error) { return z, nil } +// MinCustomWindowSize is the minimum window size that can be sent to NewWriterWindow. +const MinCustomWindowSize = flate.MinCustomWindowSize + +// MaxCustomWindowSize is the maximum custom window that can be sent to NewWriterWindow. +const MaxCustomWindowSize = flate.MaxCustomWindowSize + +// NewWriterWindow returns a new Writer compressing data with a custom window size. +// windowSize must be from MinCustomWindowSize to MaxCustomWindowSize. +func NewWriterWindow(w io.Writer, windowSize int) (*Writer, error) { + if windowSize < MinCustomWindowSize { + return nil, errors.New("gzip: requested window size less than MinWindowSize") + } + if windowSize > MaxCustomWindowSize { + return nil, errors.New("gzip: requested window size bigger than MaxCustomWindowSize") + } + + z := new(Writer) + z.init(w, -windowSize) + return z, nil +} + func (z *Writer) init(w io.Writer, level int) { compressor := z.compressor if level != StatelessCompression { diff --git a/vendor/github.com/klauspost/compress/s2/dict.go b/vendor/github.com/klauspost/compress/s2/dict.go index 24f7ce80b..f125ad096 100644 --- a/vendor/github.com/klauspost/compress/s2/dict.go +++ b/vendor/github.com/klauspost/compress/s2/dict.go @@ -106,6 +106,25 @@ func MakeDict(data []byte, searchStart []byte) *Dict { return &d } +// MakeDictManual will create a dictionary. +// 'data' must be at least MinDictSize and less than or equal to MaxDictSize. +// A manual first repeat index into data must be provided. +// It must be less than len(data)-8. +func MakeDictManual(data []byte, firstIdx uint16) *Dict { + if len(data) < MinDictSize || int(firstIdx) >= len(data)-8 || len(data) > MaxDictSize { + return nil + } + var d Dict + dict := data + d.dict = dict + if cap(d.dict) < len(d.dict)+16 { + d.dict = append(make([]byte, 0, len(d.dict)+16), d.dict...) + } + + d.repeat = int(firstIdx) + return &d +} + // Encode returns the encoded form of src. The returned slice may be a sub- // slice of dst if dst was large enough to hold the entire encoded block. // Otherwise, a newly allocated slice will be returned. diff --git a/vendor/github.com/klauspost/compress/s2/encode.go b/vendor/github.com/klauspost/compress/s2/encode.go index e6c231021..0c9088adf 100644 --- a/vendor/github.com/klauspost/compress/s2/encode.go +++ b/vendor/github.com/klauspost/compress/s2/encode.go @@ -57,7 +57,7 @@ func Encode(dst, src []byte) []byte { // The function returns -1 if no improvement could be achieved. // Using actual compression will most often produce better compression than the estimate. func EstimateBlockSize(src []byte) (d int) { - if len(src) < 6 || int64(len(src)) > 0xffffffff { + if len(src) <= inputMargin || int64(len(src)) > 0xffffffff { return -1 } if len(src) <= 1024 { diff --git a/vendor/github.com/klauspost/compress/s2/encode_best.go b/vendor/github.com/klauspost/compress/s2/encode_best.go index 1d13e869a..47bac7423 100644 --- a/vendor/github.com/klauspost/compress/s2/encode_best.go +++ b/vendor/github.com/klauspost/compress/s2/encode_best.go @@ -157,6 +157,9 @@ func encodeBlockBest(dst, src []byte, dict *Dict) (d int) { return m } matchDict := func(candidate, s int, first uint32, rep bool) match { + if s >= MaxDictSrcOffset { + return match{offset: candidate, s: s} + } // Calculate offset as if in continuous array with s offset := -len(dict.dict) + candidate if best.length != 0 && best.s-best.offset == s-offset && !rep { diff --git a/vendor/github.com/klauspost/compress/s2/encode_go.go b/vendor/github.com/klauspost/compress/s2/encode_go.go index 0d39c7b0e..6b393c34d 100644 --- a/vendor/github.com/klauspost/compress/s2/encode_go.go +++ b/vendor/github.com/klauspost/compress/s2/encode_go.go @@ -316,6 +316,7 @@ func matchLen(a []byte, b []byte) int { return len(a) + checked } +// input must be > inputMargin func calcBlockSize(src []byte) (d int) { // Initialize the hash table. const ( @@ -501,6 +502,7 @@ emitRemainder: return d } +// length must be > inputMargin. func calcBlockSizeSmall(src []byte) (d int) { // Initialize the hash table. const ( diff --git a/vendor/github.com/klauspost/compress/s2/encodeblock_amd64.s b/vendor/github.com/klauspost/compress/s2/encodeblock_amd64.s index 54031aa31..5f110d194 100644 --- a/vendor/github.com/klauspost/compress/s2/encodeblock_amd64.s +++ b/vendor/github.com/klauspost/compress/s2/encodeblock_amd64.s @@ -249,15 +249,43 @@ emit_literal_done_repeat_emit_encodeBlockAsm: // matchLen XORL R11, R11 + +matchlen_loopback_16_repeat_extend_encodeBlockAsm: + CMPL R8, $0x10 + JB matchlen_match8_repeat_extend_encodeBlockAsm + MOVQ (R9)(R11*1), R10 + MOVQ 8(R9)(R11*1), R12 + XORQ (BX)(R11*1), R10 + JNZ matchlen_bsf_8_repeat_extend_encodeBlockAsm + XORQ 8(BX)(R11*1), R12 + JNZ matchlen_bsf_16repeat_extend_encodeBlockAsm + LEAL -16(R8), R8 + LEAL 16(R11), R11 + JMP matchlen_loopback_16_repeat_extend_encodeBlockAsm + +matchlen_bsf_16repeat_extend_encodeBlockAsm: +#ifdef GOAMD64_v3 + TZCNTQ R12, R12 + +#else + BSFQ R12, R12 + +#endif + SARQ $0x03, R12 + LEAL 8(R11)(R12*1), R11 + JMP repeat_extend_forward_end_encodeBlockAsm + +matchlen_match8_repeat_extend_encodeBlockAsm: CMPL R8, $0x08 JB matchlen_match4_repeat_extend_encodeBlockAsm + MOVQ (R9)(R11*1), R10 + XORQ (BX)(R11*1), R10 + JNZ matchlen_bsf_8_repeat_extend_encodeBlockAsm + LEAL -8(R8), R8 + LEAL 8(R11), R11 + JMP matchlen_match4_repeat_extend_encodeBlockAsm -matchlen_loopback_repeat_extend_encodeBlockAsm: - MOVQ (R9)(R11*1), R10 - XORQ (BX)(R11*1), R10 - TESTQ R10, R10 - JZ matchlen_loop_repeat_extend_encodeBlockAsm - +matchlen_bsf_8_repeat_extend_encodeBlockAsm: #ifdef GOAMD64_v3 TZCNTQ R10, R10 @@ -269,12 +297,6 @@ matchlen_loopback_repeat_extend_encodeBlockAsm: LEAL (R11)(R10*1), R11 JMP repeat_extend_forward_end_encodeBlockAsm -matchlen_loop_repeat_extend_encodeBlockAsm: - LEAL -8(R8), R8 - LEAL 8(R11), R11 - CMPL R8, $0x08 - JAE matchlen_loopback_repeat_extend_encodeBlockAsm - matchlen_match4_repeat_extend_encodeBlockAsm: CMPL R8, $0x04 JB matchlen_match2_repeat_extend_encodeBlockAsm @@ -851,15 +873,43 @@ match_nolit_loop_encodeBlockAsm: // matchLen XORL R9, R9 + +matchlen_loopback_16_match_nolit_encodeBlockAsm: + CMPL SI, $0x10 + JB matchlen_match8_match_nolit_encodeBlockAsm + MOVQ (DI)(R9*1), R8 + MOVQ 8(DI)(R9*1), R10 + XORQ (BX)(R9*1), R8 + JNZ matchlen_bsf_8_match_nolit_encodeBlockAsm + XORQ 8(BX)(R9*1), R10 + JNZ matchlen_bsf_16match_nolit_encodeBlockAsm + LEAL -16(SI), SI + LEAL 16(R9), R9 + JMP matchlen_loopback_16_match_nolit_encodeBlockAsm + +matchlen_bsf_16match_nolit_encodeBlockAsm: +#ifdef GOAMD64_v3 + TZCNTQ R10, R10 + +#else + BSFQ R10, R10 + +#endif + SARQ $0x03, R10 + LEAL 8(R9)(R10*1), R9 + JMP match_nolit_end_encodeBlockAsm + +matchlen_match8_match_nolit_encodeBlockAsm: CMPL SI, $0x08 JB matchlen_match4_match_nolit_encodeBlockAsm + MOVQ (DI)(R9*1), R8 + XORQ (BX)(R9*1), R8 + JNZ matchlen_bsf_8_match_nolit_encodeBlockAsm + LEAL -8(SI), SI + LEAL 8(R9), R9 + JMP matchlen_match4_match_nolit_encodeBlockAsm -matchlen_loopback_match_nolit_encodeBlockAsm: - MOVQ (DI)(R9*1), R8 - XORQ (BX)(R9*1), R8 - TESTQ R8, R8 - JZ matchlen_loop_match_nolit_encodeBlockAsm - +matchlen_bsf_8_match_nolit_encodeBlockAsm: #ifdef GOAMD64_v3 TZCNTQ R8, R8 @@ -871,12 +921,6 @@ matchlen_loopback_match_nolit_encodeBlockAsm: LEAL (R9)(R8*1), R9 JMP match_nolit_end_encodeBlockAsm -matchlen_loop_match_nolit_encodeBlockAsm: - LEAL -8(SI), SI - LEAL 8(R9), R9 - CMPL SI, $0x08 - JAE matchlen_loopback_match_nolit_encodeBlockAsm - matchlen_match4_match_nolit_encodeBlockAsm: CMPL SI, $0x04 JB matchlen_match2_match_nolit_encodeBlockAsm @@ -1610,15 +1654,43 @@ emit_literal_done_repeat_emit_encodeBlockAsm4MB: // matchLen XORL R11, R11 + +matchlen_loopback_16_repeat_extend_encodeBlockAsm4MB: + CMPL R8, $0x10 + JB matchlen_match8_repeat_extend_encodeBlockAsm4MB + MOVQ (R9)(R11*1), R10 + MOVQ 8(R9)(R11*1), R12 + XORQ (BX)(R11*1), R10 + JNZ matchlen_bsf_8_repeat_extend_encodeBlockAsm4MB + XORQ 8(BX)(R11*1), R12 + JNZ matchlen_bsf_16repeat_extend_encodeBlockAsm4MB + LEAL -16(R8), R8 + LEAL 16(R11), R11 + JMP matchlen_loopback_16_repeat_extend_encodeBlockAsm4MB + +matchlen_bsf_16repeat_extend_encodeBlockAsm4MB: +#ifdef GOAMD64_v3 + TZCNTQ R12, R12 + +#else + BSFQ R12, R12 + +#endif + SARQ $0x03, R12 + LEAL 8(R11)(R12*1), R11 + JMP repeat_extend_forward_end_encodeBlockAsm4MB + +matchlen_match8_repeat_extend_encodeBlockAsm4MB: CMPL R8, $0x08 JB matchlen_match4_repeat_extend_encodeBlockAsm4MB + MOVQ (R9)(R11*1), R10 + XORQ (BX)(R11*1), R10 + JNZ matchlen_bsf_8_repeat_extend_encodeBlockAsm4MB + LEAL -8(R8), R8 + LEAL 8(R11), R11 + JMP matchlen_match4_repeat_extend_encodeBlockAsm4MB -matchlen_loopback_repeat_extend_encodeBlockAsm4MB: - MOVQ (R9)(R11*1), R10 - XORQ (BX)(R11*1), R10 - TESTQ R10, R10 - JZ matchlen_loop_repeat_extend_encodeBlockAsm4MB - +matchlen_bsf_8_repeat_extend_encodeBlockAsm4MB: #ifdef GOAMD64_v3 TZCNTQ R10, R10 @@ -1630,12 +1702,6 @@ matchlen_loopback_repeat_extend_encodeBlockAsm4MB: LEAL (R11)(R10*1), R11 JMP repeat_extend_forward_end_encodeBlockAsm4MB -matchlen_loop_repeat_extend_encodeBlockAsm4MB: - LEAL -8(R8), R8 - LEAL 8(R11), R11 - CMPL R8, $0x08 - JAE matchlen_loopback_repeat_extend_encodeBlockAsm4MB - matchlen_match4_repeat_extend_encodeBlockAsm4MB: CMPL R8, $0x04 JB matchlen_match2_repeat_extend_encodeBlockAsm4MB @@ -2162,15 +2228,43 @@ match_nolit_loop_encodeBlockAsm4MB: // matchLen XORL R9, R9 + +matchlen_loopback_16_match_nolit_encodeBlockAsm4MB: + CMPL SI, $0x10 + JB matchlen_match8_match_nolit_encodeBlockAsm4MB + MOVQ (DI)(R9*1), R8 + MOVQ 8(DI)(R9*1), R10 + XORQ (BX)(R9*1), R8 + JNZ matchlen_bsf_8_match_nolit_encodeBlockAsm4MB + XORQ 8(BX)(R9*1), R10 + JNZ matchlen_bsf_16match_nolit_encodeBlockAsm4MB + LEAL -16(SI), SI + LEAL 16(R9), R9 + JMP matchlen_loopback_16_match_nolit_encodeBlockAsm4MB + +matchlen_bsf_16match_nolit_encodeBlockAsm4MB: +#ifdef GOAMD64_v3 + TZCNTQ R10, R10 + +#else + BSFQ R10, R10 + +#endif + SARQ $0x03, R10 + LEAL 8(R9)(R10*1), R9 + JMP match_nolit_end_encodeBlockAsm4MB + +matchlen_match8_match_nolit_encodeBlockAsm4MB: CMPL SI, $0x08 JB matchlen_match4_match_nolit_encodeBlockAsm4MB + MOVQ (DI)(R9*1), R8 + XORQ (BX)(R9*1), R8 + JNZ matchlen_bsf_8_match_nolit_encodeBlockAsm4MB + LEAL -8(SI), SI + LEAL 8(R9), R9 + JMP matchlen_match4_match_nolit_encodeBlockAsm4MB -matchlen_loopback_match_nolit_encodeBlockAsm4MB: - MOVQ (DI)(R9*1), R8 - XORQ (BX)(R9*1), R8 - TESTQ R8, R8 - JZ matchlen_loop_match_nolit_encodeBlockAsm4MB - +matchlen_bsf_8_match_nolit_encodeBlockAsm4MB: #ifdef GOAMD64_v3 TZCNTQ R8, R8 @@ -2182,12 +2276,6 @@ matchlen_loopback_match_nolit_encodeBlockAsm4MB: LEAL (R9)(R8*1), R9 JMP match_nolit_end_encodeBlockAsm4MB -matchlen_loop_match_nolit_encodeBlockAsm4MB: - LEAL -8(SI), SI - LEAL 8(R9), R9 - CMPL SI, $0x08 - JAE matchlen_loopback_match_nolit_encodeBlockAsm4MB - matchlen_match4_match_nolit_encodeBlockAsm4MB: CMPL SI, $0x04 JB matchlen_match2_match_nolit_encodeBlockAsm4MB @@ -2873,15 +2961,43 @@ emit_literal_done_repeat_emit_encodeBlockAsm12B: // matchLen XORL R11, R11 + +matchlen_loopback_16_repeat_extend_encodeBlockAsm12B: + CMPL R8, $0x10 + JB matchlen_match8_repeat_extend_encodeBlockAsm12B + MOVQ (R9)(R11*1), R10 + MOVQ 8(R9)(R11*1), R12 + XORQ (BX)(R11*1), R10 + JNZ matchlen_bsf_8_repeat_extend_encodeBlockAsm12B + XORQ 8(BX)(R11*1), R12 + JNZ matchlen_bsf_16repeat_extend_encodeBlockAsm12B + LEAL -16(R8), R8 + LEAL 16(R11), R11 + JMP matchlen_loopback_16_repeat_extend_encodeBlockAsm12B + +matchlen_bsf_16repeat_extend_encodeBlockAsm12B: +#ifdef GOAMD64_v3 + TZCNTQ R12, R12 + +#else + BSFQ R12, R12 + +#endif + SARQ $0x03, R12 + LEAL 8(R11)(R12*1), R11 + JMP repeat_extend_forward_end_encodeBlockAsm12B + +matchlen_match8_repeat_extend_encodeBlockAsm12B: CMPL R8, $0x08 JB matchlen_match4_repeat_extend_encodeBlockAsm12B + MOVQ (R9)(R11*1), R10 + XORQ (BX)(R11*1), R10 + JNZ matchlen_bsf_8_repeat_extend_encodeBlockAsm12B + LEAL -8(R8), R8 + LEAL 8(R11), R11 + JMP matchlen_match4_repeat_extend_encodeBlockAsm12B -matchlen_loopback_repeat_extend_encodeBlockAsm12B: - MOVQ (R9)(R11*1), R10 - XORQ (BX)(R11*1), R10 - TESTQ R10, R10 - JZ matchlen_loop_repeat_extend_encodeBlockAsm12B - +matchlen_bsf_8_repeat_extend_encodeBlockAsm12B: #ifdef GOAMD64_v3 TZCNTQ R10, R10 @@ -2893,12 +3009,6 @@ matchlen_loopback_repeat_extend_encodeBlockAsm12B: LEAL (R11)(R10*1), R11 JMP repeat_extend_forward_end_encodeBlockAsm12B -matchlen_loop_repeat_extend_encodeBlockAsm12B: - LEAL -8(R8), R8 - LEAL 8(R11), R11 - CMPL R8, $0x08 - JAE matchlen_loopback_repeat_extend_encodeBlockAsm12B - matchlen_match4_repeat_extend_encodeBlockAsm12B: CMPL R8, $0x04 JB matchlen_match2_repeat_extend_encodeBlockAsm12B @@ -3303,15 +3413,43 @@ match_nolit_loop_encodeBlockAsm12B: // matchLen XORL R9, R9 + +matchlen_loopback_16_match_nolit_encodeBlockAsm12B: + CMPL SI, $0x10 + JB matchlen_match8_match_nolit_encodeBlockAsm12B + MOVQ (DI)(R9*1), R8 + MOVQ 8(DI)(R9*1), R10 + XORQ (BX)(R9*1), R8 + JNZ matchlen_bsf_8_match_nolit_encodeBlockAsm12B + XORQ 8(BX)(R9*1), R10 + JNZ matchlen_bsf_16match_nolit_encodeBlockAsm12B + LEAL -16(SI), SI + LEAL 16(R9), R9 + JMP matchlen_loopback_16_match_nolit_encodeBlockAsm12B + +matchlen_bsf_16match_nolit_encodeBlockAsm12B: +#ifdef GOAMD64_v3 + TZCNTQ R10, R10 + +#else + BSFQ R10, R10 + +#endif + SARQ $0x03, R10 + LEAL 8(R9)(R10*1), R9 + JMP match_nolit_end_encodeBlockAsm12B + +matchlen_match8_match_nolit_encodeBlockAsm12B: CMPL SI, $0x08 JB matchlen_match4_match_nolit_encodeBlockAsm12B + MOVQ (DI)(R9*1), R8 + XORQ (BX)(R9*1), R8 + JNZ matchlen_bsf_8_match_nolit_encodeBlockAsm12B + LEAL -8(SI), SI + LEAL 8(R9), R9 + JMP matchlen_match4_match_nolit_encodeBlockAsm12B -matchlen_loopback_match_nolit_encodeBlockAsm12B: - MOVQ (DI)(R9*1), R8 - XORQ (BX)(R9*1), R8 - TESTQ R8, R8 - JZ matchlen_loop_match_nolit_encodeBlockAsm12B - +matchlen_bsf_8_match_nolit_encodeBlockAsm12B: #ifdef GOAMD64_v3 TZCNTQ R8, R8 @@ -3323,12 +3461,6 @@ matchlen_loopback_match_nolit_encodeBlockAsm12B: LEAL (R9)(R8*1), R9 JMP match_nolit_end_encodeBlockAsm12B -matchlen_loop_match_nolit_encodeBlockAsm12B: - LEAL -8(SI), SI - LEAL 8(R9), R9 - CMPL SI, $0x08 - JAE matchlen_loopback_match_nolit_encodeBlockAsm12B - matchlen_match4_match_nolit_encodeBlockAsm12B: CMPL SI, $0x04 JB matchlen_match2_match_nolit_encodeBlockAsm12B @@ -3904,15 +4036,43 @@ emit_literal_done_repeat_emit_encodeBlockAsm10B: // matchLen XORL R11, R11 + +matchlen_loopback_16_repeat_extend_encodeBlockAsm10B: + CMPL R8, $0x10 + JB matchlen_match8_repeat_extend_encodeBlockAsm10B + MOVQ (R9)(R11*1), R10 + MOVQ 8(R9)(R11*1), R12 + XORQ (BX)(R11*1), R10 + JNZ matchlen_bsf_8_repeat_extend_encodeBlockAsm10B + XORQ 8(BX)(R11*1), R12 + JNZ matchlen_bsf_16repeat_extend_encodeBlockAsm10B + LEAL -16(R8), R8 + LEAL 16(R11), R11 + JMP matchlen_loopback_16_repeat_extend_encodeBlockAsm10B + +matchlen_bsf_16repeat_extend_encodeBlockAsm10B: +#ifdef GOAMD64_v3 + TZCNTQ R12, R12 + +#else + BSFQ R12, R12 + +#endif + SARQ $0x03, R12 + LEAL 8(R11)(R12*1), R11 + JMP repeat_extend_forward_end_encodeBlockAsm10B + +matchlen_match8_repeat_extend_encodeBlockAsm10B: CMPL R8, $0x08 JB matchlen_match4_repeat_extend_encodeBlockAsm10B + MOVQ (R9)(R11*1), R10 + XORQ (BX)(R11*1), R10 + JNZ matchlen_bsf_8_repeat_extend_encodeBlockAsm10B + LEAL -8(R8), R8 + LEAL 8(R11), R11 + JMP matchlen_match4_repeat_extend_encodeBlockAsm10B -matchlen_loopback_repeat_extend_encodeBlockAsm10B: - MOVQ (R9)(R11*1), R10 - XORQ (BX)(R11*1), R10 - TESTQ R10, R10 - JZ matchlen_loop_repeat_extend_encodeBlockAsm10B - +matchlen_bsf_8_repeat_extend_encodeBlockAsm10B: #ifdef GOAMD64_v3 TZCNTQ R10, R10 @@ -3924,12 +4084,6 @@ matchlen_loopback_repeat_extend_encodeBlockAsm10B: LEAL (R11)(R10*1), R11 JMP repeat_extend_forward_end_encodeBlockAsm10B -matchlen_loop_repeat_extend_encodeBlockAsm10B: - LEAL -8(R8), R8 - LEAL 8(R11), R11 - CMPL R8, $0x08 - JAE matchlen_loopback_repeat_extend_encodeBlockAsm10B - matchlen_match4_repeat_extend_encodeBlockAsm10B: CMPL R8, $0x04 JB matchlen_match2_repeat_extend_encodeBlockAsm10B @@ -4334,15 +4488,43 @@ match_nolit_loop_encodeBlockAsm10B: // matchLen XORL R9, R9 + +matchlen_loopback_16_match_nolit_encodeBlockAsm10B: + CMPL SI, $0x10 + JB matchlen_match8_match_nolit_encodeBlockAsm10B + MOVQ (DI)(R9*1), R8 + MOVQ 8(DI)(R9*1), R10 + XORQ (BX)(R9*1), R8 + JNZ matchlen_bsf_8_match_nolit_encodeBlockAsm10B + XORQ 8(BX)(R9*1), R10 + JNZ matchlen_bsf_16match_nolit_encodeBlockAsm10B + LEAL -16(SI), SI + LEAL 16(R9), R9 + JMP matchlen_loopback_16_match_nolit_encodeBlockAsm10B + +matchlen_bsf_16match_nolit_encodeBlockAsm10B: +#ifdef GOAMD64_v3 + TZCNTQ R10, R10 + +#else + BSFQ R10, R10 + +#endif + SARQ $0x03, R10 + LEAL 8(R9)(R10*1), R9 + JMP match_nolit_end_encodeBlockAsm10B + +matchlen_match8_match_nolit_encodeBlockAsm10B: CMPL SI, $0x08 JB matchlen_match4_match_nolit_encodeBlockAsm10B + MOVQ (DI)(R9*1), R8 + XORQ (BX)(R9*1), R8 + JNZ matchlen_bsf_8_match_nolit_encodeBlockAsm10B + LEAL -8(SI), SI + LEAL 8(R9), R9 + JMP matchlen_match4_match_nolit_encodeBlockAsm10B -matchlen_loopback_match_nolit_encodeBlockAsm10B: - MOVQ (DI)(R9*1), R8 - XORQ (BX)(R9*1), R8 - TESTQ R8, R8 - JZ matchlen_loop_match_nolit_encodeBlockAsm10B - +matchlen_bsf_8_match_nolit_encodeBlockAsm10B: #ifdef GOAMD64_v3 TZCNTQ R8, R8 @@ -4354,12 +4536,6 @@ matchlen_loopback_match_nolit_encodeBlockAsm10B: LEAL (R9)(R8*1), R9 JMP match_nolit_end_encodeBlockAsm10B -matchlen_loop_match_nolit_encodeBlockAsm10B: - LEAL -8(SI), SI - LEAL 8(R9), R9 - CMPL SI, $0x08 - JAE matchlen_loopback_match_nolit_encodeBlockAsm10B - matchlen_match4_match_nolit_encodeBlockAsm10B: CMPL SI, $0x04 JB matchlen_match2_match_nolit_encodeBlockAsm10B @@ -4935,15 +5111,43 @@ emit_literal_done_repeat_emit_encodeBlockAsm8B: // matchLen XORL R11, R11 + +matchlen_loopback_16_repeat_extend_encodeBlockAsm8B: + CMPL R8, $0x10 + JB matchlen_match8_repeat_extend_encodeBlockAsm8B + MOVQ (R9)(R11*1), R10 + MOVQ 8(R9)(R11*1), R12 + XORQ (BX)(R11*1), R10 + JNZ matchlen_bsf_8_repeat_extend_encodeBlockAsm8B + XORQ 8(BX)(R11*1), R12 + JNZ matchlen_bsf_16repeat_extend_encodeBlockAsm8B + LEAL -16(R8), R8 + LEAL 16(R11), R11 + JMP matchlen_loopback_16_repeat_extend_encodeBlockAsm8B + +matchlen_bsf_16repeat_extend_encodeBlockAsm8B: +#ifdef GOAMD64_v3 + TZCNTQ R12, R12 + +#else + BSFQ R12, R12 + +#endif + SARQ $0x03, R12 + LEAL 8(R11)(R12*1), R11 + JMP repeat_extend_forward_end_encodeBlockAsm8B + +matchlen_match8_repeat_extend_encodeBlockAsm8B: CMPL R8, $0x08 JB matchlen_match4_repeat_extend_encodeBlockAsm8B + MOVQ (R9)(R11*1), R10 + XORQ (BX)(R11*1), R10 + JNZ matchlen_bsf_8_repeat_extend_encodeBlockAsm8B + LEAL -8(R8), R8 + LEAL 8(R11), R11 + JMP matchlen_match4_repeat_extend_encodeBlockAsm8B -matchlen_loopback_repeat_extend_encodeBlockAsm8B: - MOVQ (R9)(R11*1), R10 - XORQ (BX)(R11*1), R10 - TESTQ R10, R10 - JZ matchlen_loop_repeat_extend_encodeBlockAsm8B - +matchlen_bsf_8_repeat_extend_encodeBlockAsm8B: #ifdef GOAMD64_v3 TZCNTQ R10, R10 @@ -4955,12 +5159,6 @@ matchlen_loopback_repeat_extend_encodeBlockAsm8B: LEAL (R11)(R10*1), R11 JMP repeat_extend_forward_end_encodeBlockAsm8B -matchlen_loop_repeat_extend_encodeBlockAsm8B: - LEAL -8(R8), R8 - LEAL 8(R11), R11 - CMPL R8, $0x08 - JAE matchlen_loopback_repeat_extend_encodeBlockAsm8B - matchlen_match4_repeat_extend_encodeBlockAsm8B: CMPL R8, $0x04 JB matchlen_match2_repeat_extend_encodeBlockAsm8B @@ -5351,15 +5549,43 @@ match_nolit_loop_encodeBlockAsm8B: // matchLen XORL R9, R9 + +matchlen_loopback_16_match_nolit_encodeBlockAsm8B: + CMPL SI, $0x10 + JB matchlen_match8_match_nolit_encodeBlockAsm8B + MOVQ (DI)(R9*1), R8 + MOVQ 8(DI)(R9*1), R10 + XORQ (BX)(R9*1), R8 + JNZ matchlen_bsf_8_match_nolit_encodeBlockAsm8B + XORQ 8(BX)(R9*1), R10 + JNZ matchlen_bsf_16match_nolit_encodeBlockAsm8B + LEAL -16(SI), SI + LEAL 16(R9), R9 + JMP matchlen_loopback_16_match_nolit_encodeBlockAsm8B + +matchlen_bsf_16match_nolit_encodeBlockAsm8B: +#ifdef GOAMD64_v3 + TZCNTQ R10, R10 + +#else + BSFQ R10, R10 + +#endif + SARQ $0x03, R10 + LEAL 8(R9)(R10*1), R9 + JMP match_nolit_end_encodeBlockAsm8B + +matchlen_match8_match_nolit_encodeBlockAsm8B: CMPL SI, $0x08 JB matchlen_match4_match_nolit_encodeBlockAsm8B + MOVQ (DI)(R9*1), R8 + XORQ (BX)(R9*1), R8 + JNZ matchlen_bsf_8_match_nolit_encodeBlockAsm8B + LEAL -8(SI), SI + LEAL 8(R9), R9 + JMP matchlen_match4_match_nolit_encodeBlockAsm8B -matchlen_loopback_match_nolit_encodeBlockAsm8B: - MOVQ (DI)(R9*1), R8 - XORQ (BX)(R9*1), R8 - TESTQ R8, R8 - JZ matchlen_loop_match_nolit_encodeBlockAsm8B - +matchlen_bsf_8_match_nolit_encodeBlockAsm8B: #ifdef GOAMD64_v3 TZCNTQ R8, R8 @@ -5371,12 +5597,6 @@ matchlen_loopback_match_nolit_encodeBlockAsm8B: LEAL (R9)(R8*1), R9 JMP match_nolit_end_encodeBlockAsm8B -matchlen_loop_match_nolit_encodeBlockAsm8B: - LEAL -8(SI), SI - LEAL 8(R9), R9 - CMPL SI, $0x08 - JAE matchlen_loopback_match_nolit_encodeBlockAsm8B - matchlen_match4_match_nolit_encodeBlockAsm8B: CMPL SI, $0x04 JB matchlen_match2_match_nolit_encodeBlockAsm8B @@ -5854,15 +6074,43 @@ match_dst_size_check_encodeBetterBlockAsm: // matchLen XORL R11, R11 + +matchlen_loopback_16_match_nolit_encodeBetterBlockAsm: + CMPL DI, $0x10 + JB matchlen_match8_match_nolit_encodeBetterBlockAsm + MOVQ (R8)(R11*1), R10 + MOVQ 8(R8)(R11*1), R12 + XORQ (R9)(R11*1), R10 + JNZ matchlen_bsf_8_match_nolit_encodeBetterBlockAsm + XORQ 8(R9)(R11*1), R12 + JNZ matchlen_bsf_16match_nolit_encodeBetterBlockAsm + LEAL -16(DI), DI + LEAL 16(R11), R11 + JMP matchlen_loopback_16_match_nolit_encodeBetterBlockAsm + +matchlen_bsf_16match_nolit_encodeBetterBlockAsm: +#ifdef GOAMD64_v3 + TZCNTQ R12, R12 + +#else + BSFQ R12, R12 + +#endif + SARQ $0x03, R12 + LEAL 8(R11)(R12*1), R11 + JMP match_nolit_end_encodeBetterBlockAsm + +matchlen_match8_match_nolit_encodeBetterBlockAsm: CMPL DI, $0x08 JB matchlen_match4_match_nolit_encodeBetterBlockAsm + MOVQ (R8)(R11*1), R10 + XORQ (R9)(R11*1), R10 + JNZ matchlen_bsf_8_match_nolit_encodeBetterBlockAsm + LEAL -8(DI), DI + LEAL 8(R11), R11 + JMP matchlen_match4_match_nolit_encodeBetterBlockAsm -matchlen_loopback_match_nolit_encodeBetterBlockAsm: - MOVQ (R8)(R11*1), R10 - XORQ (R9)(R11*1), R10 - TESTQ R10, R10 - JZ matchlen_loop_match_nolit_encodeBetterBlockAsm - +matchlen_bsf_8_match_nolit_encodeBetterBlockAsm: #ifdef GOAMD64_v3 TZCNTQ R10, R10 @@ -5874,12 +6122,6 @@ matchlen_loopback_match_nolit_encodeBetterBlockAsm: LEAL (R11)(R10*1), R11 JMP match_nolit_end_encodeBetterBlockAsm -matchlen_loop_match_nolit_encodeBetterBlockAsm: - LEAL -8(DI), DI - LEAL 8(R11), R11 - CMPL DI, $0x08 - JAE matchlen_loopback_match_nolit_encodeBetterBlockAsm - matchlen_match4_match_nolit_encodeBetterBlockAsm: CMPL DI, $0x04 JB matchlen_match2_match_nolit_encodeBetterBlockAsm @@ -6926,15 +7168,43 @@ match_dst_size_check_encodeBetterBlockAsm4MB: // matchLen XORL R11, R11 + +matchlen_loopback_16_match_nolit_encodeBetterBlockAsm4MB: + CMPL DI, $0x10 + JB matchlen_match8_match_nolit_encodeBetterBlockAsm4MB + MOVQ (R8)(R11*1), R10 + MOVQ 8(R8)(R11*1), R12 + XORQ (R9)(R11*1), R10 + JNZ matchlen_bsf_8_match_nolit_encodeBetterBlockAsm4MB + XORQ 8(R9)(R11*1), R12 + JNZ matchlen_bsf_16match_nolit_encodeBetterBlockAsm4MB + LEAL -16(DI), DI + LEAL 16(R11), R11 + JMP matchlen_loopback_16_match_nolit_encodeBetterBlockAsm4MB + +matchlen_bsf_16match_nolit_encodeBetterBlockAsm4MB: +#ifdef GOAMD64_v3 + TZCNTQ R12, R12 + +#else + BSFQ R12, R12 + +#endif + SARQ $0x03, R12 + LEAL 8(R11)(R12*1), R11 + JMP match_nolit_end_encodeBetterBlockAsm4MB + +matchlen_match8_match_nolit_encodeBetterBlockAsm4MB: CMPL DI, $0x08 JB matchlen_match4_match_nolit_encodeBetterBlockAsm4MB + MOVQ (R8)(R11*1), R10 + XORQ (R9)(R11*1), R10 + JNZ matchlen_bsf_8_match_nolit_encodeBetterBlockAsm4MB + LEAL -8(DI), DI + LEAL 8(R11), R11 + JMP matchlen_match4_match_nolit_encodeBetterBlockAsm4MB -matchlen_loopback_match_nolit_encodeBetterBlockAsm4MB: - MOVQ (R8)(R11*1), R10 - XORQ (R9)(R11*1), R10 - TESTQ R10, R10 - JZ matchlen_loop_match_nolit_encodeBetterBlockAsm4MB - +matchlen_bsf_8_match_nolit_encodeBetterBlockAsm4MB: #ifdef GOAMD64_v3 TZCNTQ R10, R10 @@ -6946,12 +7216,6 @@ matchlen_loopback_match_nolit_encodeBetterBlockAsm4MB: LEAL (R11)(R10*1), R11 JMP match_nolit_end_encodeBetterBlockAsm4MB -matchlen_loop_match_nolit_encodeBetterBlockAsm4MB: - LEAL -8(DI), DI - LEAL 8(R11), R11 - CMPL DI, $0x08 - JAE matchlen_loopback_match_nolit_encodeBetterBlockAsm4MB - matchlen_match4_match_nolit_encodeBetterBlockAsm4MB: CMPL DI, $0x04 JB matchlen_match2_match_nolit_encodeBetterBlockAsm4MB @@ -7924,15 +8188,43 @@ match_dst_size_check_encodeBetterBlockAsm12B: // matchLen XORL R11, R11 + +matchlen_loopback_16_match_nolit_encodeBetterBlockAsm12B: + CMPL DI, $0x10 + JB matchlen_match8_match_nolit_encodeBetterBlockAsm12B + MOVQ (R8)(R11*1), R10 + MOVQ 8(R8)(R11*1), R12 + XORQ (R9)(R11*1), R10 + JNZ matchlen_bsf_8_match_nolit_encodeBetterBlockAsm12B + XORQ 8(R9)(R11*1), R12 + JNZ matchlen_bsf_16match_nolit_encodeBetterBlockAsm12B + LEAL -16(DI), DI + LEAL 16(R11), R11 + JMP matchlen_loopback_16_match_nolit_encodeBetterBlockAsm12B + +matchlen_bsf_16match_nolit_encodeBetterBlockAsm12B: +#ifdef GOAMD64_v3 + TZCNTQ R12, R12 + +#else + BSFQ R12, R12 + +#endif + SARQ $0x03, R12 + LEAL 8(R11)(R12*1), R11 + JMP match_nolit_end_encodeBetterBlockAsm12B + +matchlen_match8_match_nolit_encodeBetterBlockAsm12B: CMPL DI, $0x08 JB matchlen_match4_match_nolit_encodeBetterBlockAsm12B + MOVQ (R8)(R11*1), R10 + XORQ (R9)(R11*1), R10 + JNZ matchlen_bsf_8_match_nolit_encodeBetterBlockAsm12B + LEAL -8(DI), DI + LEAL 8(R11), R11 + JMP matchlen_match4_match_nolit_encodeBetterBlockAsm12B -matchlen_loopback_match_nolit_encodeBetterBlockAsm12B: - MOVQ (R8)(R11*1), R10 - XORQ (R9)(R11*1), R10 - TESTQ R10, R10 - JZ matchlen_loop_match_nolit_encodeBetterBlockAsm12B - +matchlen_bsf_8_match_nolit_encodeBetterBlockAsm12B: #ifdef GOAMD64_v3 TZCNTQ R10, R10 @@ -7944,12 +8236,6 @@ matchlen_loopback_match_nolit_encodeBetterBlockAsm12B: LEAL (R11)(R10*1), R11 JMP match_nolit_end_encodeBetterBlockAsm12B -matchlen_loop_match_nolit_encodeBetterBlockAsm12B: - LEAL -8(DI), DI - LEAL 8(R11), R11 - CMPL DI, $0x08 - JAE matchlen_loopback_match_nolit_encodeBetterBlockAsm12B - matchlen_match4_match_nolit_encodeBetterBlockAsm12B: CMPL DI, $0x04 JB matchlen_match2_match_nolit_encodeBetterBlockAsm12B @@ -8775,15 +9061,43 @@ match_dst_size_check_encodeBetterBlockAsm10B: // matchLen XORL R11, R11 + +matchlen_loopback_16_match_nolit_encodeBetterBlockAsm10B: + CMPL DI, $0x10 + JB matchlen_match8_match_nolit_encodeBetterBlockAsm10B + MOVQ (R8)(R11*1), R10 + MOVQ 8(R8)(R11*1), R12 + XORQ (R9)(R11*1), R10 + JNZ matchlen_bsf_8_match_nolit_encodeBetterBlockAsm10B + XORQ 8(R9)(R11*1), R12 + JNZ matchlen_bsf_16match_nolit_encodeBetterBlockAsm10B + LEAL -16(DI), DI + LEAL 16(R11), R11 + JMP matchlen_loopback_16_match_nolit_encodeBetterBlockAsm10B + +matchlen_bsf_16match_nolit_encodeBetterBlockAsm10B: +#ifdef GOAMD64_v3 + TZCNTQ R12, R12 + +#else + BSFQ R12, R12 + +#endif + SARQ $0x03, R12 + LEAL 8(R11)(R12*1), R11 + JMP match_nolit_end_encodeBetterBlockAsm10B + +matchlen_match8_match_nolit_encodeBetterBlockAsm10B: CMPL DI, $0x08 JB matchlen_match4_match_nolit_encodeBetterBlockAsm10B + MOVQ (R8)(R11*1), R10 + XORQ (R9)(R11*1), R10 + JNZ matchlen_bsf_8_match_nolit_encodeBetterBlockAsm10B + LEAL -8(DI), DI + LEAL 8(R11), R11 + JMP matchlen_match4_match_nolit_encodeBetterBlockAsm10B -matchlen_loopback_match_nolit_encodeBetterBlockAsm10B: - MOVQ (R8)(R11*1), R10 - XORQ (R9)(R11*1), R10 - TESTQ R10, R10 - JZ matchlen_loop_match_nolit_encodeBetterBlockAsm10B - +matchlen_bsf_8_match_nolit_encodeBetterBlockAsm10B: #ifdef GOAMD64_v3 TZCNTQ R10, R10 @@ -8795,12 +9109,6 @@ matchlen_loopback_match_nolit_encodeBetterBlockAsm10B: LEAL (R11)(R10*1), R11 JMP match_nolit_end_encodeBetterBlockAsm10B -matchlen_loop_match_nolit_encodeBetterBlockAsm10B: - LEAL -8(DI), DI - LEAL 8(R11), R11 - CMPL DI, $0x08 - JAE matchlen_loopback_match_nolit_encodeBetterBlockAsm10B - matchlen_match4_match_nolit_encodeBetterBlockAsm10B: CMPL DI, $0x04 JB matchlen_match2_match_nolit_encodeBetterBlockAsm10B @@ -9626,15 +9934,43 @@ match_dst_size_check_encodeBetterBlockAsm8B: // matchLen XORL R11, R11 + +matchlen_loopback_16_match_nolit_encodeBetterBlockAsm8B: + CMPL DI, $0x10 + JB matchlen_match8_match_nolit_encodeBetterBlockAsm8B + MOVQ (R8)(R11*1), R10 + MOVQ 8(R8)(R11*1), R12 + XORQ (R9)(R11*1), R10 + JNZ matchlen_bsf_8_match_nolit_encodeBetterBlockAsm8B + XORQ 8(R9)(R11*1), R12 + JNZ matchlen_bsf_16match_nolit_encodeBetterBlockAsm8B + LEAL -16(DI), DI + LEAL 16(R11), R11 + JMP matchlen_loopback_16_match_nolit_encodeBetterBlockAsm8B + +matchlen_bsf_16match_nolit_encodeBetterBlockAsm8B: +#ifdef GOAMD64_v3 + TZCNTQ R12, R12 + +#else + BSFQ R12, R12 + +#endif + SARQ $0x03, R12 + LEAL 8(R11)(R12*1), R11 + JMP match_nolit_end_encodeBetterBlockAsm8B + +matchlen_match8_match_nolit_encodeBetterBlockAsm8B: CMPL DI, $0x08 JB matchlen_match4_match_nolit_encodeBetterBlockAsm8B + MOVQ (R8)(R11*1), R10 + XORQ (R9)(R11*1), R10 + JNZ matchlen_bsf_8_match_nolit_encodeBetterBlockAsm8B + LEAL -8(DI), DI + LEAL 8(R11), R11 + JMP matchlen_match4_match_nolit_encodeBetterBlockAsm8B -matchlen_loopback_match_nolit_encodeBetterBlockAsm8B: - MOVQ (R8)(R11*1), R10 - XORQ (R9)(R11*1), R10 - TESTQ R10, R10 - JZ matchlen_loop_match_nolit_encodeBetterBlockAsm8B - +matchlen_bsf_8_match_nolit_encodeBetterBlockAsm8B: #ifdef GOAMD64_v3 TZCNTQ R10, R10 @@ -9646,12 +9982,6 @@ matchlen_loopback_match_nolit_encodeBetterBlockAsm8B: LEAL (R11)(R10*1), R11 JMP match_nolit_end_encodeBetterBlockAsm8B -matchlen_loop_match_nolit_encodeBetterBlockAsm8B: - LEAL -8(DI), DI - LEAL 8(R11), R11 - CMPL DI, $0x08 - JAE matchlen_loopback_match_nolit_encodeBetterBlockAsm8B - matchlen_match4_match_nolit_encodeBetterBlockAsm8B: CMPL DI, $0x04 JB matchlen_match2_match_nolit_encodeBetterBlockAsm8B @@ -10575,15 +10905,43 @@ emit_literal_done_repeat_emit_encodeSnappyBlockAsm: // matchLen XORL R10, R10 + +matchlen_loopback_16_repeat_extend_encodeSnappyBlockAsm: + CMPL DI, $0x10 + JB matchlen_match8_repeat_extend_encodeSnappyBlockAsm + MOVQ (R8)(R10*1), R9 + MOVQ 8(R8)(R10*1), R11 + XORQ (BX)(R10*1), R9 + JNZ matchlen_bsf_8_repeat_extend_encodeSnappyBlockAsm + XORQ 8(BX)(R10*1), R11 + JNZ matchlen_bsf_16repeat_extend_encodeSnappyBlockAsm + LEAL -16(DI), DI + LEAL 16(R10), R10 + JMP matchlen_loopback_16_repeat_extend_encodeSnappyBlockAsm + +matchlen_bsf_16repeat_extend_encodeSnappyBlockAsm: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL 8(R10)(R11*1), R10 + JMP repeat_extend_forward_end_encodeSnappyBlockAsm + +matchlen_match8_repeat_extend_encodeSnappyBlockAsm: CMPL DI, $0x08 JB matchlen_match4_repeat_extend_encodeSnappyBlockAsm + MOVQ (R8)(R10*1), R9 + XORQ (BX)(R10*1), R9 + JNZ matchlen_bsf_8_repeat_extend_encodeSnappyBlockAsm + LEAL -8(DI), DI + LEAL 8(R10), R10 + JMP matchlen_match4_repeat_extend_encodeSnappyBlockAsm -matchlen_loopback_repeat_extend_encodeSnappyBlockAsm: - MOVQ (R8)(R10*1), R9 - XORQ (BX)(R10*1), R9 - TESTQ R9, R9 - JZ matchlen_loop_repeat_extend_encodeSnappyBlockAsm - +matchlen_bsf_8_repeat_extend_encodeSnappyBlockAsm: #ifdef GOAMD64_v3 TZCNTQ R9, R9 @@ -10595,12 +10953,6 @@ matchlen_loopback_repeat_extend_encodeSnappyBlockAsm: LEAL (R10)(R9*1), R10 JMP repeat_extend_forward_end_encodeSnappyBlockAsm -matchlen_loop_repeat_extend_encodeSnappyBlockAsm: - LEAL -8(DI), DI - LEAL 8(R10), R10 - CMPL DI, $0x08 - JAE matchlen_loopback_repeat_extend_encodeSnappyBlockAsm - matchlen_match4_repeat_extend_encodeSnappyBlockAsm: CMPL DI, $0x04 JB matchlen_match2_repeat_extend_encodeSnappyBlockAsm @@ -10897,15 +11249,43 @@ match_nolit_loop_encodeSnappyBlockAsm: // matchLen XORL R9, R9 + +matchlen_loopback_16_match_nolit_encodeSnappyBlockAsm: + CMPL SI, $0x10 + JB matchlen_match8_match_nolit_encodeSnappyBlockAsm + MOVQ (DI)(R9*1), R8 + MOVQ 8(DI)(R9*1), R10 + XORQ (BX)(R9*1), R8 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBlockAsm + XORQ 8(BX)(R9*1), R10 + JNZ matchlen_bsf_16match_nolit_encodeSnappyBlockAsm + LEAL -16(SI), SI + LEAL 16(R9), R9 + JMP matchlen_loopback_16_match_nolit_encodeSnappyBlockAsm + +matchlen_bsf_16match_nolit_encodeSnappyBlockAsm: +#ifdef GOAMD64_v3 + TZCNTQ R10, R10 + +#else + BSFQ R10, R10 + +#endif + SARQ $0x03, R10 + LEAL 8(R9)(R10*1), R9 + JMP match_nolit_end_encodeSnappyBlockAsm + +matchlen_match8_match_nolit_encodeSnappyBlockAsm: CMPL SI, $0x08 JB matchlen_match4_match_nolit_encodeSnappyBlockAsm + MOVQ (DI)(R9*1), R8 + XORQ (BX)(R9*1), R8 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBlockAsm + LEAL -8(SI), SI + LEAL 8(R9), R9 + JMP matchlen_match4_match_nolit_encodeSnappyBlockAsm -matchlen_loopback_match_nolit_encodeSnappyBlockAsm: - MOVQ (DI)(R9*1), R8 - XORQ (BX)(R9*1), R8 - TESTQ R8, R8 - JZ matchlen_loop_match_nolit_encodeSnappyBlockAsm - +matchlen_bsf_8_match_nolit_encodeSnappyBlockAsm: #ifdef GOAMD64_v3 TZCNTQ R8, R8 @@ -10917,12 +11297,6 @@ matchlen_loopback_match_nolit_encodeSnappyBlockAsm: LEAL (R9)(R8*1), R9 JMP match_nolit_end_encodeSnappyBlockAsm -matchlen_loop_match_nolit_encodeSnappyBlockAsm: - LEAL -8(SI), SI - LEAL 8(R9), R9 - CMPL SI, $0x08 - JAE matchlen_loopback_match_nolit_encodeSnappyBlockAsm - matchlen_match4_match_nolit_encodeSnappyBlockAsm: CMPL SI, $0x04 JB matchlen_match2_match_nolit_encodeSnappyBlockAsm @@ -11437,15 +11811,43 @@ emit_literal_done_repeat_emit_encodeSnappyBlockAsm64K: // matchLen XORL R10, R10 + +matchlen_loopback_16_repeat_extend_encodeSnappyBlockAsm64K: + CMPL DI, $0x10 + JB matchlen_match8_repeat_extend_encodeSnappyBlockAsm64K + MOVQ (R8)(R10*1), R9 + MOVQ 8(R8)(R10*1), R11 + XORQ (BX)(R10*1), R9 + JNZ matchlen_bsf_8_repeat_extend_encodeSnappyBlockAsm64K + XORQ 8(BX)(R10*1), R11 + JNZ matchlen_bsf_16repeat_extend_encodeSnappyBlockAsm64K + LEAL -16(DI), DI + LEAL 16(R10), R10 + JMP matchlen_loopback_16_repeat_extend_encodeSnappyBlockAsm64K + +matchlen_bsf_16repeat_extend_encodeSnappyBlockAsm64K: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL 8(R10)(R11*1), R10 + JMP repeat_extend_forward_end_encodeSnappyBlockAsm64K + +matchlen_match8_repeat_extend_encodeSnappyBlockAsm64K: CMPL DI, $0x08 JB matchlen_match4_repeat_extend_encodeSnappyBlockAsm64K + MOVQ (R8)(R10*1), R9 + XORQ (BX)(R10*1), R9 + JNZ matchlen_bsf_8_repeat_extend_encodeSnappyBlockAsm64K + LEAL -8(DI), DI + LEAL 8(R10), R10 + JMP matchlen_match4_repeat_extend_encodeSnappyBlockAsm64K -matchlen_loopback_repeat_extend_encodeSnappyBlockAsm64K: - MOVQ (R8)(R10*1), R9 - XORQ (BX)(R10*1), R9 - TESTQ R9, R9 - JZ matchlen_loop_repeat_extend_encodeSnappyBlockAsm64K - +matchlen_bsf_8_repeat_extend_encodeSnappyBlockAsm64K: #ifdef GOAMD64_v3 TZCNTQ R9, R9 @@ -11457,12 +11859,6 @@ matchlen_loopback_repeat_extend_encodeSnappyBlockAsm64K: LEAL (R10)(R9*1), R10 JMP repeat_extend_forward_end_encodeSnappyBlockAsm64K -matchlen_loop_repeat_extend_encodeSnappyBlockAsm64K: - LEAL -8(DI), DI - LEAL 8(R10), R10 - CMPL DI, $0x08 - JAE matchlen_loopback_repeat_extend_encodeSnappyBlockAsm64K - matchlen_match4_repeat_extend_encodeSnappyBlockAsm64K: CMPL DI, $0x04 JB matchlen_match2_repeat_extend_encodeSnappyBlockAsm64K @@ -11719,15 +12115,43 @@ match_nolit_loop_encodeSnappyBlockAsm64K: // matchLen XORL R9, R9 + +matchlen_loopback_16_match_nolit_encodeSnappyBlockAsm64K: + CMPL SI, $0x10 + JB matchlen_match8_match_nolit_encodeSnappyBlockAsm64K + MOVQ (DI)(R9*1), R8 + MOVQ 8(DI)(R9*1), R10 + XORQ (BX)(R9*1), R8 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBlockAsm64K + XORQ 8(BX)(R9*1), R10 + JNZ matchlen_bsf_16match_nolit_encodeSnappyBlockAsm64K + LEAL -16(SI), SI + LEAL 16(R9), R9 + JMP matchlen_loopback_16_match_nolit_encodeSnappyBlockAsm64K + +matchlen_bsf_16match_nolit_encodeSnappyBlockAsm64K: +#ifdef GOAMD64_v3 + TZCNTQ R10, R10 + +#else + BSFQ R10, R10 + +#endif + SARQ $0x03, R10 + LEAL 8(R9)(R10*1), R9 + JMP match_nolit_end_encodeSnappyBlockAsm64K + +matchlen_match8_match_nolit_encodeSnappyBlockAsm64K: CMPL SI, $0x08 JB matchlen_match4_match_nolit_encodeSnappyBlockAsm64K + MOVQ (DI)(R9*1), R8 + XORQ (BX)(R9*1), R8 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBlockAsm64K + LEAL -8(SI), SI + LEAL 8(R9), R9 + JMP matchlen_match4_match_nolit_encodeSnappyBlockAsm64K -matchlen_loopback_match_nolit_encodeSnappyBlockAsm64K: - MOVQ (DI)(R9*1), R8 - XORQ (BX)(R9*1), R8 - TESTQ R8, R8 - JZ matchlen_loop_match_nolit_encodeSnappyBlockAsm64K - +matchlen_bsf_8_match_nolit_encodeSnappyBlockAsm64K: #ifdef GOAMD64_v3 TZCNTQ R8, R8 @@ -11739,12 +12163,6 @@ matchlen_loopback_match_nolit_encodeSnappyBlockAsm64K: LEAL (R9)(R8*1), R9 JMP match_nolit_end_encodeSnappyBlockAsm64K -matchlen_loop_match_nolit_encodeSnappyBlockAsm64K: - LEAL -8(SI), SI - LEAL 8(R9), R9 - CMPL SI, $0x08 - JAE matchlen_loopback_match_nolit_encodeSnappyBlockAsm64K - matchlen_match4_match_nolit_encodeSnappyBlockAsm64K: CMPL SI, $0x04 JB matchlen_match2_match_nolit_encodeSnappyBlockAsm64K @@ -12219,15 +12637,43 @@ emit_literal_done_repeat_emit_encodeSnappyBlockAsm12B: // matchLen XORL R10, R10 + +matchlen_loopback_16_repeat_extend_encodeSnappyBlockAsm12B: + CMPL DI, $0x10 + JB matchlen_match8_repeat_extend_encodeSnappyBlockAsm12B + MOVQ (R8)(R10*1), R9 + MOVQ 8(R8)(R10*1), R11 + XORQ (BX)(R10*1), R9 + JNZ matchlen_bsf_8_repeat_extend_encodeSnappyBlockAsm12B + XORQ 8(BX)(R10*1), R11 + JNZ matchlen_bsf_16repeat_extend_encodeSnappyBlockAsm12B + LEAL -16(DI), DI + LEAL 16(R10), R10 + JMP matchlen_loopback_16_repeat_extend_encodeSnappyBlockAsm12B + +matchlen_bsf_16repeat_extend_encodeSnappyBlockAsm12B: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL 8(R10)(R11*1), R10 + JMP repeat_extend_forward_end_encodeSnappyBlockAsm12B + +matchlen_match8_repeat_extend_encodeSnappyBlockAsm12B: CMPL DI, $0x08 JB matchlen_match4_repeat_extend_encodeSnappyBlockAsm12B + MOVQ (R8)(R10*1), R9 + XORQ (BX)(R10*1), R9 + JNZ matchlen_bsf_8_repeat_extend_encodeSnappyBlockAsm12B + LEAL -8(DI), DI + LEAL 8(R10), R10 + JMP matchlen_match4_repeat_extend_encodeSnappyBlockAsm12B -matchlen_loopback_repeat_extend_encodeSnappyBlockAsm12B: - MOVQ (R8)(R10*1), R9 - XORQ (BX)(R10*1), R9 - TESTQ R9, R9 - JZ matchlen_loop_repeat_extend_encodeSnappyBlockAsm12B - +matchlen_bsf_8_repeat_extend_encodeSnappyBlockAsm12B: #ifdef GOAMD64_v3 TZCNTQ R9, R9 @@ -12239,12 +12685,6 @@ matchlen_loopback_repeat_extend_encodeSnappyBlockAsm12B: LEAL (R10)(R9*1), R10 JMP repeat_extend_forward_end_encodeSnappyBlockAsm12B -matchlen_loop_repeat_extend_encodeSnappyBlockAsm12B: - LEAL -8(DI), DI - LEAL 8(R10), R10 - CMPL DI, $0x08 - JAE matchlen_loopback_repeat_extend_encodeSnappyBlockAsm12B - matchlen_match4_repeat_extend_encodeSnappyBlockAsm12B: CMPL DI, $0x04 JB matchlen_match2_repeat_extend_encodeSnappyBlockAsm12B @@ -12501,15 +12941,43 @@ match_nolit_loop_encodeSnappyBlockAsm12B: // matchLen XORL R9, R9 + +matchlen_loopback_16_match_nolit_encodeSnappyBlockAsm12B: + CMPL SI, $0x10 + JB matchlen_match8_match_nolit_encodeSnappyBlockAsm12B + MOVQ (DI)(R9*1), R8 + MOVQ 8(DI)(R9*1), R10 + XORQ (BX)(R9*1), R8 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBlockAsm12B + XORQ 8(BX)(R9*1), R10 + JNZ matchlen_bsf_16match_nolit_encodeSnappyBlockAsm12B + LEAL -16(SI), SI + LEAL 16(R9), R9 + JMP matchlen_loopback_16_match_nolit_encodeSnappyBlockAsm12B + +matchlen_bsf_16match_nolit_encodeSnappyBlockAsm12B: +#ifdef GOAMD64_v3 + TZCNTQ R10, R10 + +#else + BSFQ R10, R10 + +#endif + SARQ $0x03, R10 + LEAL 8(R9)(R10*1), R9 + JMP match_nolit_end_encodeSnappyBlockAsm12B + +matchlen_match8_match_nolit_encodeSnappyBlockAsm12B: CMPL SI, $0x08 JB matchlen_match4_match_nolit_encodeSnappyBlockAsm12B + MOVQ (DI)(R9*1), R8 + XORQ (BX)(R9*1), R8 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBlockAsm12B + LEAL -8(SI), SI + LEAL 8(R9), R9 + JMP matchlen_match4_match_nolit_encodeSnappyBlockAsm12B -matchlen_loopback_match_nolit_encodeSnappyBlockAsm12B: - MOVQ (DI)(R9*1), R8 - XORQ (BX)(R9*1), R8 - TESTQ R8, R8 - JZ matchlen_loop_match_nolit_encodeSnappyBlockAsm12B - +matchlen_bsf_8_match_nolit_encodeSnappyBlockAsm12B: #ifdef GOAMD64_v3 TZCNTQ R8, R8 @@ -12521,12 +12989,6 @@ matchlen_loopback_match_nolit_encodeSnappyBlockAsm12B: LEAL (R9)(R8*1), R9 JMP match_nolit_end_encodeSnappyBlockAsm12B -matchlen_loop_match_nolit_encodeSnappyBlockAsm12B: - LEAL -8(SI), SI - LEAL 8(R9), R9 - CMPL SI, $0x08 - JAE matchlen_loopback_match_nolit_encodeSnappyBlockAsm12B - matchlen_match4_match_nolit_encodeSnappyBlockAsm12B: CMPL SI, $0x04 JB matchlen_match2_match_nolit_encodeSnappyBlockAsm12B @@ -13001,15 +13463,43 @@ emit_literal_done_repeat_emit_encodeSnappyBlockAsm10B: // matchLen XORL R10, R10 + +matchlen_loopback_16_repeat_extend_encodeSnappyBlockAsm10B: + CMPL DI, $0x10 + JB matchlen_match8_repeat_extend_encodeSnappyBlockAsm10B + MOVQ (R8)(R10*1), R9 + MOVQ 8(R8)(R10*1), R11 + XORQ (BX)(R10*1), R9 + JNZ matchlen_bsf_8_repeat_extend_encodeSnappyBlockAsm10B + XORQ 8(BX)(R10*1), R11 + JNZ matchlen_bsf_16repeat_extend_encodeSnappyBlockAsm10B + LEAL -16(DI), DI + LEAL 16(R10), R10 + JMP matchlen_loopback_16_repeat_extend_encodeSnappyBlockAsm10B + +matchlen_bsf_16repeat_extend_encodeSnappyBlockAsm10B: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL 8(R10)(R11*1), R10 + JMP repeat_extend_forward_end_encodeSnappyBlockAsm10B + +matchlen_match8_repeat_extend_encodeSnappyBlockAsm10B: CMPL DI, $0x08 JB matchlen_match4_repeat_extend_encodeSnappyBlockAsm10B + MOVQ (R8)(R10*1), R9 + XORQ (BX)(R10*1), R9 + JNZ matchlen_bsf_8_repeat_extend_encodeSnappyBlockAsm10B + LEAL -8(DI), DI + LEAL 8(R10), R10 + JMP matchlen_match4_repeat_extend_encodeSnappyBlockAsm10B -matchlen_loopback_repeat_extend_encodeSnappyBlockAsm10B: - MOVQ (R8)(R10*1), R9 - XORQ (BX)(R10*1), R9 - TESTQ R9, R9 - JZ matchlen_loop_repeat_extend_encodeSnappyBlockAsm10B - +matchlen_bsf_8_repeat_extend_encodeSnappyBlockAsm10B: #ifdef GOAMD64_v3 TZCNTQ R9, R9 @@ -13021,12 +13511,6 @@ matchlen_loopback_repeat_extend_encodeSnappyBlockAsm10B: LEAL (R10)(R9*1), R10 JMP repeat_extend_forward_end_encodeSnappyBlockAsm10B -matchlen_loop_repeat_extend_encodeSnappyBlockAsm10B: - LEAL -8(DI), DI - LEAL 8(R10), R10 - CMPL DI, $0x08 - JAE matchlen_loopback_repeat_extend_encodeSnappyBlockAsm10B - matchlen_match4_repeat_extend_encodeSnappyBlockAsm10B: CMPL DI, $0x04 JB matchlen_match2_repeat_extend_encodeSnappyBlockAsm10B @@ -13283,15 +13767,43 @@ match_nolit_loop_encodeSnappyBlockAsm10B: // matchLen XORL R9, R9 + +matchlen_loopback_16_match_nolit_encodeSnappyBlockAsm10B: + CMPL SI, $0x10 + JB matchlen_match8_match_nolit_encodeSnappyBlockAsm10B + MOVQ (DI)(R9*1), R8 + MOVQ 8(DI)(R9*1), R10 + XORQ (BX)(R9*1), R8 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBlockAsm10B + XORQ 8(BX)(R9*1), R10 + JNZ matchlen_bsf_16match_nolit_encodeSnappyBlockAsm10B + LEAL -16(SI), SI + LEAL 16(R9), R9 + JMP matchlen_loopback_16_match_nolit_encodeSnappyBlockAsm10B + +matchlen_bsf_16match_nolit_encodeSnappyBlockAsm10B: +#ifdef GOAMD64_v3 + TZCNTQ R10, R10 + +#else + BSFQ R10, R10 + +#endif + SARQ $0x03, R10 + LEAL 8(R9)(R10*1), R9 + JMP match_nolit_end_encodeSnappyBlockAsm10B + +matchlen_match8_match_nolit_encodeSnappyBlockAsm10B: CMPL SI, $0x08 JB matchlen_match4_match_nolit_encodeSnappyBlockAsm10B + MOVQ (DI)(R9*1), R8 + XORQ (BX)(R9*1), R8 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBlockAsm10B + LEAL -8(SI), SI + LEAL 8(R9), R9 + JMP matchlen_match4_match_nolit_encodeSnappyBlockAsm10B -matchlen_loopback_match_nolit_encodeSnappyBlockAsm10B: - MOVQ (DI)(R9*1), R8 - XORQ (BX)(R9*1), R8 - TESTQ R8, R8 - JZ matchlen_loop_match_nolit_encodeSnappyBlockAsm10B - +matchlen_bsf_8_match_nolit_encodeSnappyBlockAsm10B: #ifdef GOAMD64_v3 TZCNTQ R8, R8 @@ -13303,12 +13815,6 @@ matchlen_loopback_match_nolit_encodeSnappyBlockAsm10B: LEAL (R9)(R8*1), R9 JMP match_nolit_end_encodeSnappyBlockAsm10B -matchlen_loop_match_nolit_encodeSnappyBlockAsm10B: - LEAL -8(SI), SI - LEAL 8(R9), R9 - CMPL SI, $0x08 - JAE matchlen_loopback_match_nolit_encodeSnappyBlockAsm10B - matchlen_match4_match_nolit_encodeSnappyBlockAsm10B: CMPL SI, $0x04 JB matchlen_match2_match_nolit_encodeSnappyBlockAsm10B @@ -13783,15 +14289,43 @@ emit_literal_done_repeat_emit_encodeSnappyBlockAsm8B: // matchLen XORL R10, R10 + +matchlen_loopback_16_repeat_extend_encodeSnappyBlockAsm8B: + CMPL DI, $0x10 + JB matchlen_match8_repeat_extend_encodeSnappyBlockAsm8B + MOVQ (R8)(R10*1), R9 + MOVQ 8(R8)(R10*1), R11 + XORQ (BX)(R10*1), R9 + JNZ matchlen_bsf_8_repeat_extend_encodeSnappyBlockAsm8B + XORQ 8(BX)(R10*1), R11 + JNZ matchlen_bsf_16repeat_extend_encodeSnappyBlockAsm8B + LEAL -16(DI), DI + LEAL 16(R10), R10 + JMP matchlen_loopback_16_repeat_extend_encodeSnappyBlockAsm8B + +matchlen_bsf_16repeat_extend_encodeSnappyBlockAsm8B: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL 8(R10)(R11*1), R10 + JMP repeat_extend_forward_end_encodeSnappyBlockAsm8B + +matchlen_match8_repeat_extend_encodeSnappyBlockAsm8B: CMPL DI, $0x08 JB matchlen_match4_repeat_extend_encodeSnappyBlockAsm8B + MOVQ (R8)(R10*1), R9 + XORQ (BX)(R10*1), R9 + JNZ matchlen_bsf_8_repeat_extend_encodeSnappyBlockAsm8B + LEAL -8(DI), DI + LEAL 8(R10), R10 + JMP matchlen_match4_repeat_extend_encodeSnappyBlockAsm8B -matchlen_loopback_repeat_extend_encodeSnappyBlockAsm8B: - MOVQ (R8)(R10*1), R9 - XORQ (BX)(R10*1), R9 - TESTQ R9, R9 - JZ matchlen_loop_repeat_extend_encodeSnappyBlockAsm8B - +matchlen_bsf_8_repeat_extend_encodeSnappyBlockAsm8B: #ifdef GOAMD64_v3 TZCNTQ R9, R9 @@ -13803,12 +14337,6 @@ matchlen_loopback_repeat_extend_encodeSnappyBlockAsm8B: LEAL (R10)(R9*1), R10 JMP repeat_extend_forward_end_encodeSnappyBlockAsm8B -matchlen_loop_repeat_extend_encodeSnappyBlockAsm8B: - LEAL -8(DI), DI - LEAL 8(R10), R10 - CMPL DI, $0x08 - JAE matchlen_loopback_repeat_extend_encodeSnappyBlockAsm8B - matchlen_match4_repeat_extend_encodeSnappyBlockAsm8B: CMPL DI, $0x04 JB matchlen_match2_repeat_extend_encodeSnappyBlockAsm8B @@ -14063,15 +14591,43 @@ match_nolit_loop_encodeSnappyBlockAsm8B: // matchLen XORL R9, R9 + +matchlen_loopback_16_match_nolit_encodeSnappyBlockAsm8B: + CMPL SI, $0x10 + JB matchlen_match8_match_nolit_encodeSnappyBlockAsm8B + MOVQ (DI)(R9*1), R8 + MOVQ 8(DI)(R9*1), R10 + XORQ (BX)(R9*1), R8 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBlockAsm8B + XORQ 8(BX)(R9*1), R10 + JNZ matchlen_bsf_16match_nolit_encodeSnappyBlockAsm8B + LEAL -16(SI), SI + LEAL 16(R9), R9 + JMP matchlen_loopback_16_match_nolit_encodeSnappyBlockAsm8B + +matchlen_bsf_16match_nolit_encodeSnappyBlockAsm8B: +#ifdef GOAMD64_v3 + TZCNTQ R10, R10 + +#else + BSFQ R10, R10 + +#endif + SARQ $0x03, R10 + LEAL 8(R9)(R10*1), R9 + JMP match_nolit_end_encodeSnappyBlockAsm8B + +matchlen_match8_match_nolit_encodeSnappyBlockAsm8B: CMPL SI, $0x08 JB matchlen_match4_match_nolit_encodeSnappyBlockAsm8B + MOVQ (DI)(R9*1), R8 + XORQ (BX)(R9*1), R8 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBlockAsm8B + LEAL -8(SI), SI + LEAL 8(R9), R9 + JMP matchlen_match4_match_nolit_encodeSnappyBlockAsm8B -matchlen_loopback_match_nolit_encodeSnappyBlockAsm8B: - MOVQ (DI)(R9*1), R8 - XORQ (BX)(R9*1), R8 - TESTQ R8, R8 - JZ matchlen_loop_match_nolit_encodeSnappyBlockAsm8B - +matchlen_bsf_8_match_nolit_encodeSnappyBlockAsm8B: #ifdef GOAMD64_v3 TZCNTQ R8, R8 @@ -14083,12 +14639,6 @@ matchlen_loopback_match_nolit_encodeSnappyBlockAsm8B: LEAL (R9)(R8*1), R9 JMP match_nolit_end_encodeSnappyBlockAsm8B -matchlen_loop_match_nolit_encodeSnappyBlockAsm8B: - LEAL -8(SI), SI - LEAL 8(R9), R9 - CMPL SI, $0x08 - JAE matchlen_loopback_match_nolit_encodeSnappyBlockAsm8B - matchlen_match4_match_nolit_encodeSnappyBlockAsm8B: CMPL SI, $0x04 JB matchlen_match2_match_nolit_encodeSnappyBlockAsm8B @@ -14473,15 +15023,43 @@ match_dst_size_check_encodeSnappyBetterBlockAsm: // matchLen XORL R11, R11 + +matchlen_loopback_16_match_nolit_encodeSnappyBetterBlockAsm: + CMPL DI, $0x10 + JB matchlen_match8_match_nolit_encodeSnappyBetterBlockAsm + MOVQ (R8)(R11*1), R10 + MOVQ 8(R8)(R11*1), R12 + XORQ (R9)(R11*1), R10 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBetterBlockAsm + XORQ 8(R9)(R11*1), R12 + JNZ matchlen_bsf_16match_nolit_encodeSnappyBetterBlockAsm + LEAL -16(DI), DI + LEAL 16(R11), R11 + JMP matchlen_loopback_16_match_nolit_encodeSnappyBetterBlockAsm + +matchlen_bsf_16match_nolit_encodeSnappyBetterBlockAsm: +#ifdef GOAMD64_v3 + TZCNTQ R12, R12 + +#else + BSFQ R12, R12 + +#endif + SARQ $0x03, R12 + LEAL 8(R11)(R12*1), R11 + JMP match_nolit_end_encodeSnappyBetterBlockAsm + +matchlen_match8_match_nolit_encodeSnappyBetterBlockAsm: CMPL DI, $0x08 JB matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm + MOVQ (R8)(R11*1), R10 + XORQ (R9)(R11*1), R10 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBetterBlockAsm + LEAL -8(DI), DI + LEAL 8(R11), R11 + JMP matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm -matchlen_loopback_match_nolit_encodeSnappyBetterBlockAsm: - MOVQ (R8)(R11*1), R10 - XORQ (R9)(R11*1), R10 - TESTQ R10, R10 - JZ matchlen_loop_match_nolit_encodeSnappyBetterBlockAsm - +matchlen_bsf_8_match_nolit_encodeSnappyBetterBlockAsm: #ifdef GOAMD64_v3 TZCNTQ R10, R10 @@ -14493,12 +15071,6 @@ matchlen_loopback_match_nolit_encodeSnappyBetterBlockAsm: LEAL (R11)(R10*1), R11 JMP match_nolit_end_encodeSnappyBetterBlockAsm -matchlen_loop_match_nolit_encodeSnappyBetterBlockAsm: - LEAL -8(DI), DI - LEAL 8(R11), R11 - CMPL DI, $0x08 - JAE matchlen_loopback_match_nolit_encodeSnappyBetterBlockAsm - matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm: CMPL DI, $0x04 JB matchlen_match2_match_nolit_encodeSnappyBetterBlockAsm @@ -15096,15 +15668,43 @@ match_dst_size_check_encodeSnappyBetterBlockAsm64K: // matchLen XORL R11, R11 + +matchlen_loopback_16_match_nolit_encodeSnappyBetterBlockAsm64K: + CMPL DI, $0x10 + JB matchlen_match8_match_nolit_encodeSnappyBetterBlockAsm64K + MOVQ (R8)(R11*1), R10 + MOVQ 8(R8)(R11*1), R12 + XORQ (R9)(R11*1), R10 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBetterBlockAsm64K + XORQ 8(R9)(R11*1), R12 + JNZ matchlen_bsf_16match_nolit_encodeSnappyBetterBlockAsm64K + LEAL -16(DI), DI + LEAL 16(R11), R11 + JMP matchlen_loopback_16_match_nolit_encodeSnappyBetterBlockAsm64K + +matchlen_bsf_16match_nolit_encodeSnappyBetterBlockAsm64K: +#ifdef GOAMD64_v3 + TZCNTQ R12, R12 + +#else + BSFQ R12, R12 + +#endif + SARQ $0x03, R12 + LEAL 8(R11)(R12*1), R11 + JMP match_nolit_end_encodeSnappyBetterBlockAsm64K + +matchlen_match8_match_nolit_encodeSnappyBetterBlockAsm64K: CMPL DI, $0x08 JB matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm64K + MOVQ (R8)(R11*1), R10 + XORQ (R9)(R11*1), R10 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBetterBlockAsm64K + LEAL -8(DI), DI + LEAL 8(R11), R11 + JMP matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm64K -matchlen_loopback_match_nolit_encodeSnappyBetterBlockAsm64K: - MOVQ (R8)(R11*1), R10 - XORQ (R9)(R11*1), R10 - TESTQ R10, R10 - JZ matchlen_loop_match_nolit_encodeSnappyBetterBlockAsm64K - +matchlen_bsf_8_match_nolit_encodeSnappyBetterBlockAsm64K: #ifdef GOAMD64_v3 TZCNTQ R10, R10 @@ -15116,12 +15716,6 @@ matchlen_loopback_match_nolit_encodeSnappyBetterBlockAsm64K: LEAL (R11)(R10*1), R11 JMP match_nolit_end_encodeSnappyBetterBlockAsm64K -matchlen_loop_match_nolit_encodeSnappyBetterBlockAsm64K: - LEAL -8(DI), DI - LEAL 8(R11), R11 - CMPL DI, $0x08 - JAE matchlen_loopback_match_nolit_encodeSnappyBetterBlockAsm64K - matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm64K: CMPL DI, $0x04 JB matchlen_match2_match_nolit_encodeSnappyBetterBlockAsm64K @@ -15654,15 +16248,43 @@ match_dst_size_check_encodeSnappyBetterBlockAsm12B: // matchLen XORL R11, R11 + +matchlen_loopback_16_match_nolit_encodeSnappyBetterBlockAsm12B: + CMPL DI, $0x10 + JB matchlen_match8_match_nolit_encodeSnappyBetterBlockAsm12B + MOVQ (R8)(R11*1), R10 + MOVQ 8(R8)(R11*1), R12 + XORQ (R9)(R11*1), R10 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBetterBlockAsm12B + XORQ 8(R9)(R11*1), R12 + JNZ matchlen_bsf_16match_nolit_encodeSnappyBetterBlockAsm12B + LEAL -16(DI), DI + LEAL 16(R11), R11 + JMP matchlen_loopback_16_match_nolit_encodeSnappyBetterBlockAsm12B + +matchlen_bsf_16match_nolit_encodeSnappyBetterBlockAsm12B: +#ifdef GOAMD64_v3 + TZCNTQ R12, R12 + +#else + BSFQ R12, R12 + +#endif + SARQ $0x03, R12 + LEAL 8(R11)(R12*1), R11 + JMP match_nolit_end_encodeSnappyBetterBlockAsm12B + +matchlen_match8_match_nolit_encodeSnappyBetterBlockAsm12B: CMPL DI, $0x08 JB matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm12B + MOVQ (R8)(R11*1), R10 + XORQ (R9)(R11*1), R10 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBetterBlockAsm12B + LEAL -8(DI), DI + LEAL 8(R11), R11 + JMP matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm12B -matchlen_loopback_match_nolit_encodeSnappyBetterBlockAsm12B: - MOVQ (R8)(R11*1), R10 - XORQ (R9)(R11*1), R10 - TESTQ R10, R10 - JZ matchlen_loop_match_nolit_encodeSnappyBetterBlockAsm12B - +matchlen_bsf_8_match_nolit_encodeSnappyBetterBlockAsm12B: #ifdef GOAMD64_v3 TZCNTQ R10, R10 @@ -15674,12 +16296,6 @@ matchlen_loopback_match_nolit_encodeSnappyBetterBlockAsm12B: LEAL (R11)(R10*1), R11 JMP match_nolit_end_encodeSnappyBetterBlockAsm12B -matchlen_loop_match_nolit_encodeSnappyBetterBlockAsm12B: - LEAL -8(DI), DI - LEAL 8(R11), R11 - CMPL DI, $0x08 - JAE matchlen_loopback_match_nolit_encodeSnappyBetterBlockAsm12B - matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm12B: CMPL DI, $0x04 JB matchlen_match2_match_nolit_encodeSnappyBetterBlockAsm12B @@ -16212,15 +16828,43 @@ match_dst_size_check_encodeSnappyBetterBlockAsm10B: // matchLen XORL R11, R11 + +matchlen_loopback_16_match_nolit_encodeSnappyBetterBlockAsm10B: + CMPL DI, $0x10 + JB matchlen_match8_match_nolit_encodeSnappyBetterBlockAsm10B + MOVQ (R8)(R11*1), R10 + MOVQ 8(R8)(R11*1), R12 + XORQ (R9)(R11*1), R10 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBetterBlockAsm10B + XORQ 8(R9)(R11*1), R12 + JNZ matchlen_bsf_16match_nolit_encodeSnappyBetterBlockAsm10B + LEAL -16(DI), DI + LEAL 16(R11), R11 + JMP matchlen_loopback_16_match_nolit_encodeSnappyBetterBlockAsm10B + +matchlen_bsf_16match_nolit_encodeSnappyBetterBlockAsm10B: +#ifdef GOAMD64_v3 + TZCNTQ R12, R12 + +#else + BSFQ R12, R12 + +#endif + SARQ $0x03, R12 + LEAL 8(R11)(R12*1), R11 + JMP match_nolit_end_encodeSnappyBetterBlockAsm10B + +matchlen_match8_match_nolit_encodeSnappyBetterBlockAsm10B: CMPL DI, $0x08 JB matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm10B + MOVQ (R8)(R11*1), R10 + XORQ (R9)(R11*1), R10 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBetterBlockAsm10B + LEAL -8(DI), DI + LEAL 8(R11), R11 + JMP matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm10B -matchlen_loopback_match_nolit_encodeSnappyBetterBlockAsm10B: - MOVQ (R8)(R11*1), R10 - XORQ (R9)(R11*1), R10 - TESTQ R10, R10 - JZ matchlen_loop_match_nolit_encodeSnappyBetterBlockAsm10B - +matchlen_bsf_8_match_nolit_encodeSnappyBetterBlockAsm10B: #ifdef GOAMD64_v3 TZCNTQ R10, R10 @@ -16232,12 +16876,6 @@ matchlen_loopback_match_nolit_encodeSnappyBetterBlockAsm10B: LEAL (R11)(R10*1), R11 JMP match_nolit_end_encodeSnappyBetterBlockAsm10B -matchlen_loop_match_nolit_encodeSnappyBetterBlockAsm10B: - LEAL -8(DI), DI - LEAL 8(R11), R11 - CMPL DI, $0x08 - JAE matchlen_loopback_match_nolit_encodeSnappyBetterBlockAsm10B - matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm10B: CMPL DI, $0x04 JB matchlen_match2_match_nolit_encodeSnappyBetterBlockAsm10B @@ -16770,15 +17408,43 @@ match_dst_size_check_encodeSnappyBetterBlockAsm8B: // matchLen XORL R11, R11 + +matchlen_loopback_16_match_nolit_encodeSnappyBetterBlockAsm8B: + CMPL DI, $0x10 + JB matchlen_match8_match_nolit_encodeSnappyBetterBlockAsm8B + MOVQ (R8)(R11*1), R10 + MOVQ 8(R8)(R11*1), R12 + XORQ (R9)(R11*1), R10 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBetterBlockAsm8B + XORQ 8(R9)(R11*1), R12 + JNZ matchlen_bsf_16match_nolit_encodeSnappyBetterBlockAsm8B + LEAL -16(DI), DI + LEAL 16(R11), R11 + JMP matchlen_loopback_16_match_nolit_encodeSnappyBetterBlockAsm8B + +matchlen_bsf_16match_nolit_encodeSnappyBetterBlockAsm8B: +#ifdef GOAMD64_v3 + TZCNTQ R12, R12 + +#else + BSFQ R12, R12 + +#endif + SARQ $0x03, R12 + LEAL 8(R11)(R12*1), R11 + JMP match_nolit_end_encodeSnappyBetterBlockAsm8B + +matchlen_match8_match_nolit_encodeSnappyBetterBlockAsm8B: CMPL DI, $0x08 JB matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm8B + MOVQ (R8)(R11*1), R10 + XORQ (R9)(R11*1), R10 + JNZ matchlen_bsf_8_match_nolit_encodeSnappyBetterBlockAsm8B + LEAL -8(DI), DI + LEAL 8(R11), R11 + JMP matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm8B -matchlen_loopback_match_nolit_encodeSnappyBetterBlockAsm8B: - MOVQ (R8)(R11*1), R10 - XORQ (R9)(R11*1), R10 - TESTQ R10, R10 - JZ matchlen_loop_match_nolit_encodeSnappyBetterBlockAsm8B - +matchlen_bsf_8_match_nolit_encodeSnappyBetterBlockAsm8B: #ifdef GOAMD64_v3 TZCNTQ R10, R10 @@ -16790,12 +17456,6 @@ matchlen_loopback_match_nolit_encodeSnappyBetterBlockAsm8B: LEAL (R11)(R10*1), R11 JMP match_nolit_end_encodeSnappyBetterBlockAsm8B -matchlen_loop_match_nolit_encodeSnappyBetterBlockAsm8B: - LEAL -8(DI), DI - LEAL 8(R11), R11 - CMPL DI, $0x08 - JAE matchlen_loopback_match_nolit_encodeSnappyBetterBlockAsm8B - matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm8B: CMPL DI, $0x04 JB matchlen_match2_match_nolit_encodeSnappyBetterBlockAsm8B @@ -17343,15 +18003,43 @@ emit_literal_done_repeat_emit_calcBlockSize: // matchLen XORL R10, R10 + +matchlen_loopback_16_repeat_extend_calcBlockSize: + CMPL DI, $0x10 + JB matchlen_match8_repeat_extend_calcBlockSize + MOVQ (R8)(R10*1), R9 + MOVQ 8(R8)(R10*1), R11 + XORQ (BX)(R10*1), R9 + JNZ matchlen_bsf_8_repeat_extend_calcBlockSize + XORQ 8(BX)(R10*1), R11 + JNZ matchlen_bsf_16repeat_extend_calcBlockSize + LEAL -16(DI), DI + LEAL 16(R10), R10 + JMP matchlen_loopback_16_repeat_extend_calcBlockSize + +matchlen_bsf_16repeat_extend_calcBlockSize: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL 8(R10)(R11*1), R10 + JMP repeat_extend_forward_end_calcBlockSize + +matchlen_match8_repeat_extend_calcBlockSize: CMPL DI, $0x08 JB matchlen_match4_repeat_extend_calcBlockSize + MOVQ (R8)(R10*1), R9 + XORQ (BX)(R10*1), R9 + JNZ matchlen_bsf_8_repeat_extend_calcBlockSize + LEAL -8(DI), DI + LEAL 8(R10), R10 + JMP matchlen_match4_repeat_extend_calcBlockSize -matchlen_loopback_repeat_extend_calcBlockSize: - MOVQ (R8)(R10*1), R9 - XORQ (BX)(R10*1), R9 - TESTQ R9, R9 - JZ matchlen_loop_repeat_extend_calcBlockSize - +matchlen_bsf_8_repeat_extend_calcBlockSize: #ifdef GOAMD64_v3 TZCNTQ R9, R9 @@ -17363,12 +18051,6 @@ matchlen_loopback_repeat_extend_calcBlockSize: LEAL (R10)(R9*1), R10 JMP repeat_extend_forward_end_calcBlockSize -matchlen_loop_repeat_extend_calcBlockSize: - LEAL -8(DI), DI - LEAL 8(R10), R10 - CMPL DI, $0x08 - JAE matchlen_loopback_repeat_extend_calcBlockSize - matchlen_match4_repeat_extend_calcBlockSize: CMPL DI, $0x04 JB matchlen_match2_repeat_extend_calcBlockSize @@ -17554,15 +18236,43 @@ match_nolit_loop_calcBlockSize: // matchLen XORL R9, R9 + +matchlen_loopback_16_match_nolit_calcBlockSize: + CMPL SI, $0x10 + JB matchlen_match8_match_nolit_calcBlockSize + MOVQ (DI)(R9*1), R8 + MOVQ 8(DI)(R9*1), R10 + XORQ (BX)(R9*1), R8 + JNZ matchlen_bsf_8_match_nolit_calcBlockSize + XORQ 8(BX)(R9*1), R10 + JNZ matchlen_bsf_16match_nolit_calcBlockSize + LEAL -16(SI), SI + LEAL 16(R9), R9 + JMP matchlen_loopback_16_match_nolit_calcBlockSize + +matchlen_bsf_16match_nolit_calcBlockSize: +#ifdef GOAMD64_v3 + TZCNTQ R10, R10 + +#else + BSFQ R10, R10 + +#endif + SARQ $0x03, R10 + LEAL 8(R9)(R10*1), R9 + JMP match_nolit_end_calcBlockSize + +matchlen_match8_match_nolit_calcBlockSize: CMPL SI, $0x08 JB matchlen_match4_match_nolit_calcBlockSize + MOVQ (DI)(R9*1), R8 + XORQ (BX)(R9*1), R8 + JNZ matchlen_bsf_8_match_nolit_calcBlockSize + LEAL -8(SI), SI + LEAL 8(R9), R9 + JMP matchlen_match4_match_nolit_calcBlockSize -matchlen_loopback_match_nolit_calcBlockSize: - MOVQ (DI)(R9*1), R8 - XORQ (BX)(R9*1), R8 - TESTQ R8, R8 - JZ matchlen_loop_match_nolit_calcBlockSize - +matchlen_bsf_8_match_nolit_calcBlockSize: #ifdef GOAMD64_v3 TZCNTQ R8, R8 @@ -17574,12 +18284,6 @@ matchlen_loopback_match_nolit_calcBlockSize: LEAL (R9)(R8*1), R9 JMP match_nolit_end_calcBlockSize -matchlen_loop_match_nolit_calcBlockSize: - LEAL -8(SI), SI - LEAL 8(R9), R9 - CMPL SI, $0x08 - JAE matchlen_loopback_match_nolit_calcBlockSize - matchlen_match4_match_nolit_calcBlockSize: CMPL SI, $0x04 JB matchlen_match2_match_nolit_calcBlockSize @@ -17872,15 +18576,43 @@ emit_literal_done_repeat_emit_calcBlockSizeSmall: // matchLen XORL R10, R10 + +matchlen_loopback_16_repeat_extend_calcBlockSizeSmall: + CMPL DI, $0x10 + JB matchlen_match8_repeat_extend_calcBlockSizeSmall + MOVQ (R8)(R10*1), R9 + MOVQ 8(R8)(R10*1), R11 + XORQ (BX)(R10*1), R9 + JNZ matchlen_bsf_8_repeat_extend_calcBlockSizeSmall + XORQ 8(BX)(R10*1), R11 + JNZ matchlen_bsf_16repeat_extend_calcBlockSizeSmall + LEAL -16(DI), DI + LEAL 16(R10), R10 + JMP matchlen_loopback_16_repeat_extend_calcBlockSizeSmall + +matchlen_bsf_16repeat_extend_calcBlockSizeSmall: +#ifdef GOAMD64_v3 + TZCNTQ R11, R11 + +#else + BSFQ R11, R11 + +#endif + SARQ $0x03, R11 + LEAL 8(R10)(R11*1), R10 + JMP repeat_extend_forward_end_calcBlockSizeSmall + +matchlen_match8_repeat_extend_calcBlockSizeSmall: CMPL DI, $0x08 JB matchlen_match4_repeat_extend_calcBlockSizeSmall + MOVQ (R8)(R10*1), R9 + XORQ (BX)(R10*1), R9 + JNZ matchlen_bsf_8_repeat_extend_calcBlockSizeSmall + LEAL -8(DI), DI + LEAL 8(R10), R10 + JMP matchlen_match4_repeat_extend_calcBlockSizeSmall -matchlen_loopback_repeat_extend_calcBlockSizeSmall: - MOVQ (R8)(R10*1), R9 - XORQ (BX)(R10*1), R9 - TESTQ R9, R9 - JZ matchlen_loop_repeat_extend_calcBlockSizeSmall - +matchlen_bsf_8_repeat_extend_calcBlockSizeSmall: #ifdef GOAMD64_v3 TZCNTQ R9, R9 @@ -17892,12 +18624,6 @@ matchlen_loopback_repeat_extend_calcBlockSizeSmall: LEAL (R10)(R9*1), R10 JMP repeat_extend_forward_end_calcBlockSizeSmall -matchlen_loop_repeat_extend_calcBlockSizeSmall: - LEAL -8(DI), DI - LEAL 8(R10), R10 - CMPL DI, $0x08 - JAE matchlen_loopback_repeat_extend_calcBlockSizeSmall - matchlen_match4_repeat_extend_calcBlockSizeSmall: CMPL DI, $0x04 JB matchlen_match2_repeat_extend_calcBlockSizeSmall @@ -18053,15 +18779,43 @@ match_nolit_loop_calcBlockSizeSmall: // matchLen XORL R9, R9 + +matchlen_loopback_16_match_nolit_calcBlockSizeSmall: + CMPL SI, $0x10 + JB matchlen_match8_match_nolit_calcBlockSizeSmall + MOVQ (DI)(R9*1), R8 + MOVQ 8(DI)(R9*1), R10 + XORQ (BX)(R9*1), R8 + JNZ matchlen_bsf_8_match_nolit_calcBlockSizeSmall + XORQ 8(BX)(R9*1), R10 + JNZ matchlen_bsf_16match_nolit_calcBlockSizeSmall + LEAL -16(SI), SI + LEAL 16(R9), R9 + JMP matchlen_loopback_16_match_nolit_calcBlockSizeSmall + +matchlen_bsf_16match_nolit_calcBlockSizeSmall: +#ifdef GOAMD64_v3 + TZCNTQ R10, R10 + +#else + BSFQ R10, R10 + +#endif + SARQ $0x03, R10 + LEAL 8(R9)(R10*1), R9 + JMP match_nolit_end_calcBlockSizeSmall + +matchlen_match8_match_nolit_calcBlockSizeSmall: CMPL SI, $0x08 JB matchlen_match4_match_nolit_calcBlockSizeSmall + MOVQ (DI)(R9*1), R8 + XORQ (BX)(R9*1), R8 + JNZ matchlen_bsf_8_match_nolit_calcBlockSizeSmall + LEAL -8(SI), SI + LEAL 8(R9), R9 + JMP matchlen_match4_match_nolit_calcBlockSizeSmall -matchlen_loopback_match_nolit_calcBlockSizeSmall: - MOVQ (DI)(R9*1), R8 - XORQ (BX)(R9*1), R8 - TESTQ R8, R8 - JZ matchlen_loop_match_nolit_calcBlockSizeSmall - +matchlen_bsf_8_match_nolit_calcBlockSizeSmall: #ifdef GOAMD64_v3 TZCNTQ R8, R8 @@ -18073,12 +18827,6 @@ matchlen_loopback_match_nolit_calcBlockSizeSmall: LEAL (R9)(R8*1), R9 JMP match_nolit_end_calcBlockSizeSmall -matchlen_loop_match_nolit_calcBlockSizeSmall: - LEAL -8(SI), SI - LEAL 8(R9), R9 - CMPL SI, $0x08 - JAE matchlen_loopback_match_nolit_calcBlockSizeSmall - matchlen_match4_match_nolit_calcBlockSizeSmall: CMPL SI, $0x04 JB matchlen_match2_match_nolit_calcBlockSizeSmall @@ -18840,15 +19588,43 @@ TEXT ·matchLen(SB), NOSPLIT, $0-56 // matchLen XORL SI, SI + +matchlen_loopback_16_standalone: + CMPL DX, $0x10 + JB matchlen_match8_standalone + MOVQ (AX)(SI*1), BX + MOVQ 8(AX)(SI*1), DI + XORQ (CX)(SI*1), BX + JNZ matchlen_bsf_8_standalone + XORQ 8(CX)(SI*1), DI + JNZ matchlen_bsf_16standalone + LEAL -16(DX), DX + LEAL 16(SI), SI + JMP matchlen_loopback_16_standalone + +matchlen_bsf_16standalone: +#ifdef GOAMD64_v3 + TZCNTQ DI, DI + +#else + BSFQ DI, DI + +#endif + SARQ $0x03, DI + LEAL 8(SI)(DI*1), SI + JMP gen_match_len_end + +matchlen_match8_standalone: CMPL DX, $0x08 JB matchlen_match4_standalone + MOVQ (AX)(SI*1), BX + XORQ (CX)(SI*1), BX + JNZ matchlen_bsf_8_standalone + LEAL -8(DX), DX + LEAL 8(SI), SI + JMP matchlen_match4_standalone -matchlen_loopback_standalone: - MOVQ (AX)(SI*1), BX - XORQ (CX)(SI*1), BX - TESTQ BX, BX - JZ matchlen_loop_standalone - +matchlen_bsf_8_standalone: #ifdef GOAMD64_v3 TZCNTQ BX, BX @@ -18860,12 +19636,6 @@ matchlen_loopback_standalone: LEAL (SI)(BX*1), SI JMP gen_match_len_end -matchlen_loop_standalone: - LEAL -8(DX), DX - LEAL 8(SI), SI - CMPL DX, $0x08 - JAE matchlen_loopback_standalone - matchlen_match4_standalone: CMPL DX, $0x04 JB matchlen_match2_standalone diff --git a/vendor/github.com/klauspost/compress/s2/index.go b/vendor/github.com/klauspost/compress/s2/index.go index dd9ecfe71..18a4f7acd 100644 --- a/vendor/github.com/klauspost/compress/s2/index.go +++ b/vendor/github.com/klauspost/compress/s2/index.go @@ -511,24 +511,22 @@ func IndexStream(r io.Reader) ([]byte, error) { // JSON returns the index as JSON text. func (i *Index) JSON() []byte { + type offset struct { + CompressedOffset int64 `json:"compressed"` + UncompressedOffset int64 `json:"uncompressed"` + } x := struct { - TotalUncompressed int64 `json:"total_uncompressed"` // Total Uncompressed size if known. Will be -1 if unknown. - TotalCompressed int64 `json:"total_compressed"` // Total Compressed size if known. Will be -1 if unknown. - Offsets []struct { - CompressedOffset int64 `json:"compressed"` - UncompressedOffset int64 `json:"uncompressed"` - } `json:"offsets"` - EstBlockUncomp int64 `json:"est_block_uncompressed"` + TotalUncompressed int64 `json:"total_uncompressed"` // Total Uncompressed size if known. Will be -1 if unknown. + TotalCompressed int64 `json:"total_compressed"` // Total Compressed size if known. Will be -1 if unknown. + Offsets []offset `json:"offsets"` + EstBlockUncomp int64 `json:"est_block_uncompressed"` }{ TotalUncompressed: i.TotalUncompressed, TotalCompressed: i.TotalCompressed, EstBlockUncomp: i.estBlockUncomp, } for _, v := range i.info { - x.Offsets = append(x.Offsets, struct { - CompressedOffset int64 `json:"compressed"` - UncompressedOffset int64 `json:"uncompressed"` - }{CompressedOffset: v.compressedOffset, UncompressedOffset: v.uncompressedOffset}) + x.Offsets = append(x.Offsets, offset{CompressedOffset: v.compressedOffset, UncompressedOffset: v.uncompressedOffset}) } b, _ := json.MarshalIndent(x, "", " ") return b diff --git a/vendor/modules.txt b/vendor/modules.txt index 7c6bf96c3..1fe29b32d 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -28,16 +28,10 @@ codeberg.org/gruf/go-errors/v2 # codeberg.org/gruf/go-fastcopy v1.1.2 ## explicit; go 1.17 codeberg.org/gruf/go-fastcopy -# codeberg.org/gruf/go-fastpath v1.0.3 -## explicit; go 1.14 -codeberg.org/gruf/go-fastpath # codeberg.org/gruf/go-fastpath/v2 v2.0.0 ## explicit; go 1.14 codeberg.org/gruf/go-fastpath/v2 -# codeberg.org/gruf/go-hashenc v1.0.2 -## explicit; go 1.16 -codeberg.org/gruf/go-hashenc -# codeberg.org/gruf/go-iotools v0.0.0-20230601182242-d933b07dcbef +# codeberg.org/gruf/go-iotools v0.0.0-20230811115124-5d4223615a7f ## explicit; go 1.19 codeberg.org/gruf/go-iotools # codeberg.org/gruf/go-kv v1.6.4 @@ -53,21 +47,17 @@ codeberg.org/gruf/go-mangler # codeberg.org/gruf/go-maps v1.0.3 ## explicit; go 1.19 codeberg.org/gruf/go-maps -# codeberg.org/gruf/go-mutexes v1.1.5 +# codeberg.org/gruf/go-mutexes v1.2.0 => ../go-mutexes ## explicit; go 1.14 codeberg.org/gruf/go-mutexes -# codeberg.org/gruf/go-pools v1.1.0 -## explicit; go 1.16 -codeberg.org/gruf/go-pools # codeberg.org/gruf/go-runners v1.6.1 ## explicit; go 1.19 codeberg.org/gruf/go-runners # codeberg.org/gruf/go-sched v1.2.3 ## explicit; go 1.19 codeberg.org/gruf/go-sched -# codeberg.org/gruf/go-store/v2 v2.2.2 +# codeberg.org/gruf/go-store/v2 v2.2.4 ## explicit; go 1.19 -codeberg.org/gruf/go-store/v2/kv codeberg.org/gruf/go-store/v2/storage codeberg.org/gruf/go-store/v2/util # github.com/DmitriyVTitov/size v1.5.0 @@ -348,7 +338,7 @@ github.com/json-iterator/go # github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 ## explicit github.com/kballard/go-shellquote -# github.com/klauspost/compress v1.16.7 +# github.com/klauspost/compress v1.17.2 ## explicit; go 1.18 github.com/klauspost/compress/flate github.com/klauspost/compress/gzip @@ -1082,3 +1072,4 @@ modernc.org/token # mvdan.cc/xurls/v2 v2.5.0 ## explicit; go 1.19 mvdan.cc/xurls/v2 +# codeberg.org/gruf/go-mutexes => ../go-mutexes