From 2477bd9afd3feba7d07fc5e7aaccff88c3044aaa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 13:06:46 +0000 Subject: [PATCH] Bump github.com/alicebob/miniredis/v2 from 2.22.0 to 2.23.1 Bumps [github.com/alicebob/miniredis/v2](https://github.com/alicebob/miniredis) from 2.22.0 to 2.23.1. - [Release notes](https://github.com/alicebob/miniredis/releases) - [Changelog](https://github.com/alicebob/miniredis/blob/master/CHANGELOG.md) - [Commits](https://github.com/alicebob/miniredis/compare/v2.22.0...v2.23.1) --- updated-dependencies: - dependency-name: github.com/alicebob/miniredis/v2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 +- go.sum | 8 +- .../alicebob/miniredis/v2/.gitignore | 2 + .../alicebob/miniredis/v2/CHANGELOG.md | 16 + .../alicebob/miniredis/v2/README.md | 12 +- .../alicebob/miniredis/v2/cmd_cluster.go | 9 +- .../alicebob/miniredis/v2/cmd_command.go | 2039 ++++++++++++++++- .../alicebob/miniredis/v2/cmd_connection.go | 81 +- .../alicebob/miniredis/v2/cmd_generic.go | 108 +- .../alicebob/miniredis/v2/cmd_geo.go | 178 +- .../alicebob/miniredis/v2/cmd_hash.go | 124 +- .../alicebob/miniredis/v2/cmd_hll.go | 2 +- .../alicebob/miniredis/v2/cmd_info.go | 40 + .../alicebob/miniredis/v2/cmd_list.go | 363 ++- .../alicebob/miniredis/v2/cmd_server.go | 6 +- .../alicebob/miniredis/v2/cmd_set.go | 68 +- .../alicebob/miniredis/v2/cmd_sorted_set.go | 210 +- .../alicebob/miniredis/v2/cmd_stream.go | 642 +++++- .../alicebob/miniredis/v2/cmd_string.go | 213 +- .../github.com/alicebob/miniredis/v2/geo.go | 8 +- .../github.com/alicebob/miniredis/v2/lua.go | 2 + .../alicebob/miniredis/v2/miniredis.go | 26 +- .../github.com/alicebob/miniredis/v2/opts.go | 25 +- .../github.com/alicebob/miniredis/v2/redis.go | 12 +- .../alicebob/miniredis/v2/server/server.go | 33 +- .../alicebob/miniredis/v2/stream.go | 94 +- vendor/github.com/yuin/gopher-lua/README.rst | 1 + vendor/github.com/yuin/gopher-lua/_vm.go | 2 +- vendor/github.com/yuin/gopher-lua/ast/expr.go | 1 + vendor/github.com/yuin/gopher-lua/baselib.go | 13 +- vendor/github.com/yuin/gopher-lua/compile.go | 10 +- vendor/github.com/yuin/gopher-lua/config.go | 7 + vendor/github.com/yuin/gopher-lua/loadlib.go | 3 + vendor/github.com/yuin/gopher-lua/oslib.go | 37 +- .../github.com/yuin/gopher-lua/parse/Makefile | 3 + .../github.com/yuin/gopher-lua/parse/lexer.go | 14 +- .../yuin/gopher-lua/parse/parser.go | 897 +++++--- .../yuin/gopher-lua/parse/parser.go.y | 3 + vendor/github.com/yuin/gopher-lua/pm/pm.go | 2 +- vendor/github.com/yuin/gopher-lua/table.go | 2 +- vendor/github.com/yuin/gopher-lua/vm.go | 2 +- vendor/modules.txt | 6 +- 42 files changed, 4281 insertions(+), 1047 deletions(-) create mode 100644 vendor/github.com/alicebob/miniredis/v2/cmd_info.go diff --git a/go.mod b/go.mod index 80ded6973a..81c31680ec 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/Masterminds/squirrel v0.0.0-20161115235646-20f192218cf5 github.com/NYTimes/gziphandler v1.1.1 github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 - github.com/alicebob/miniredis/v2 v2.22.0 + github.com/alicebob/miniredis/v2 v2.23.1 github.com/armon/go-metrics v0.4.0 github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b github.com/cespare/xxhash v1.1.0 @@ -184,7 +184,7 @@ require ( github.com/uber/jaeger-lib v2.4.1+incompatible // indirect github.com/vimeo/galaxycache v0.0.0-20210323154928-b7e5d71c067a // indirect github.com/weaveworks/promrus v1.2.0 // indirect - github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 // indirect + github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64 // indirect go.mongodb.org/mongo-driver v1.10.2 // indirect go.opencensus.io v0.23.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.36.0 // indirect diff --git a/go.sum b/go.sum index 88ab24d6fd..616c588686 100644 --- a/go.sum +++ b/go.sum @@ -114,8 +114,8 @@ github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAu github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk= github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= -github.com/alicebob/miniredis/v2 v2.22.0 h1:lIHHiSkEyS1MkKHCHzN+0mWrA4YdbGdimE5iZ2sHSzo= -github.com/alicebob/miniredis/v2 v2.22.0/go.mod h1:XNqvJdQJv5mSuVMc0ynneafpnL/zv52acZ6kqeS0t88= +github.com/alicebob/miniredis/v2 v2.23.1 h1:jR6wZggBxwWygeXcdNyguCOCIjPsZyNUNlAkTx2fu0U= +github.com/alicebob/miniredis/v2 v2.23.1/go.mod h1:84TWKZlxYkfgMucPBf5SOQBYJceZeQRFIaQgNMiCX6Q= github.com/aliyun/aliyun-oss-go-sdk v2.2.2+incompatible h1:9gWa46nstkJ9miBReJcN8Gq34cBFbzSpQZVVT9N09TM= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= @@ -975,8 +975,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 h1:k/gmLsJDWwWqbLCur2yWnJzwQEKRcAHXo6seXGuSwWw= -github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA= +github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64 h1:5mLPGnFdSsevFRFc9q3yYbBkB6tsm4aCwwQV/j1JQAQ= +github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= diff --git a/vendor/github.com/alicebob/miniredis/v2/.gitignore b/vendor/github.com/alicebob/miniredis/v2/.gitignore index 7ba06b06ce..8016b4be34 100644 --- a/vendor/github.com/alicebob/miniredis/v2/.gitignore +++ b/vendor/github.com/alicebob/miniredis/v2/.gitignore @@ -2,3 +2,5 @@ /integration/dump.rdb *.swp /integration/nodes.conf +.idea/ +miniredis.iml diff --git a/vendor/github.com/alicebob/miniredis/v2/CHANGELOG.md b/vendor/github.com/alicebob/miniredis/v2/CHANGELOG.md index 0c1fb87ebf..b70c3df622 100644 --- a/vendor/github.com/alicebob/miniredis/v2/CHANGELOG.md +++ b/vendor/github.com/alicebob/miniredis/v2/CHANGELOG.md @@ -1,6 +1,22 @@ ## Changelog +### v2.23.1 +- resolve $ to latest ID in XREAD (thanks @josh-hook) +- handle disconnect in blocking functions (thanks @jgirtakovskis) +- fix type conversion bug in redisToLua (thanks Sandy Harvie) +- BRPOP{LPUSH} timeout can be float since 6.0 + + +### v2.23.0 + +- basic INFO support (thanks @kirill-a-belov) +- support COUNT in SSCAN (thanks @Abdi-dd) +- test and support Go 1.19 +- support LPOS (thanks @ianstarz) +- support XPENDING, XGROUP {CREATECONSUMER,DESTROY,DELCONSUMER}, XINFO {CONSUMERS,GROUPS}, XCLAIM (thanks @sandyharvie) + + ### v2.22.0 - set miniredis.DumpMaxLineLen to get more Dump() info (thanks @afjoseph) diff --git a/vendor/github.com/alicebob/miniredis/v2/README.md b/vendor/github.com/alicebob/miniredis/v2/README.md index 60072f4e0d..cf9bde444b 100644 --- a/vendor/github.com/alicebob/miniredis/v2/README.md +++ b/vendor/github.com/alicebob/miniredis/v2/README.md @@ -64,6 +64,8 @@ Implemented commands: - FLUSHALL - FLUSHDB - TIME -- returns time.Now() or value set by SetTime() + - COMMAND -- partly + - INFO -- partly, returns only "clients" section with one field "connected_clients" - String keys (complete) - APPEND - BITCOUNT @@ -178,9 +180,15 @@ Implemented commands: - XACK - XADD - XAUTOCLAIM + - XCLAIM - XDEL - XGROUP CREATE + - XGROUP CREATECONSUMER + - XGROUP DESTROY + - XGROUP DELCONSUMER - XINFO STREAM -- partly + - XINFO GROUPS + - XINFO CONSUMERS -- partly - XLEN - XRANGE - XREAD @@ -203,8 +211,6 @@ Implemented commands: - GEORADIUS_RO - GEORADIUSBYMEMBER - GEORADIUSBYMEMBER_RO - - Server - - COMMAND -- partly - Cluster - CLUSTER SLOTS - CLUSTER KEYSLOT @@ -302,7 +308,6 @@ Commands which will probably not be implemented: - ~~CLIENT *~~ - ~~CONFIG *~~ - ~~DEBUG *~~ - - ~~INFO~~ - ~~LASTSAVE~~ - ~~MONITOR~~ - ~~ROLE~~ @@ -325,5 +330,4 @@ If you want to test Redis Sentinel have a look at [minisentinel](https://github. A changelog is kept at [CHANGELOG.md](https://github.com/alicebob/miniredis/blob/master/CHANGELOG.md). -[![Build Status](https://travis-ci.com/alicebob/miniredis.svg?branch=master)](https://travis-ci.com/alicebob/miniredis) [![Go Reference](https://pkg.go.dev/badge/github.com/alicebob/miniredis/v2.svg)](https://pkg.go.dev/github.com/alicebob/miniredis/v2) diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_cluster.go b/vendor/github.com/alicebob/miniredis/v2/cmd_cluster.go index 083c4ecf7d..9951f3dd3b 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_cluster.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_cluster.go @@ -4,13 +4,14 @@ package miniredis import ( "fmt" - "github.com/alicebob/miniredis/v2/server" "strings" + + "github.com/alicebob/miniredis/v2/server" ) // commandsCluster handles some cluster operations. func commandsCluster(m *Miniredis) { - _ = m.srv.Register("CLUSTER", m.cmdCluster) + m.srv.Register("CLUSTER", m.cmdCluster) } func (m *Miniredis) cmdCluster(c *server.Peer, cmd string, args []string) { @@ -51,14 +52,14 @@ func (m *Miniredis) cmdClusterSlots(c *server.Peer, cmd string, args []string) { }) } -//CLUSTER KEYSLOT +// CLUSTER KEYSLOT func (m *Miniredis) cmdClusterKeySlot(c *server.Peer, cmd string, args []string) { withTx(m, c, func(c *server.Peer, ctx *connCtx) { c.WriteInt(163) }) } -//CLUSTER NODES +// CLUSTER NODES func (m *Miniredis) cmdClusterNodes(c *server.Peer, cmd string, args []string) { withTx(m, c, func(c *server.Peer, ctx *connCtx) { c.WriteBulk("e7d1eecce10fd6bb5eb35b9f99a514335d9ba9ca 127.0.0.1:7000@7000 myself,master - 0 0 1 connected 0-16383") diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_command.go b/vendor/github.com/alicebob/miniredis/v2/cmd_command.go index 59abefd382..d82174f2f2 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_command.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_command.go @@ -4,15 +4,2042 @@ package miniredis import "github.com/alicebob/miniredis/v2/server" -func commandsCommand(m *Miniredis) { - _ = m.srv.Register("COMMAND", m.cmdCommand) -} - func (m *Miniredis) cmdCommand(c *server.Peer, cmd string, args []string) { // Got from redis 5.0.7 with // echo 'COMMAND' | nc redis_addr redis_port // - res := "*200\r\n*6\r\n$12\r\nhincrbyfloat\r\n:4\r\n*3\r\n+write\r\n+denyoom\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$10\r\nxreadgroup\r\n:-7\r\n*3\r\n+write\r\n+noscript\r\n+movablekeys\r\n:1\r\n:1\r\n:1\r\n*6\r\n$10\r\nsdiffstore\r\n:-3\r\n*2\r\n+write\r\n+denyoom\r\n:1\r\n:-1\r\n:1\r\n*6\r\n$8\r\nlastsave\r\n:1\r\n*2\r\n+random\r\n+fast\r\n:0\r\n:0\r\n:0\r\n*6\r\n$5\r\nsetnx\r\n:3\r\n*3\r\n+write\r\n+denyoom\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$8\r\nbzpopmax\r\n:-3\r\n*3\r\n+write\r\n+noscript\r\n+fast\r\n:1\r\n:-2\r\n:1\r\n*6\r\n$12\r\npunsubscribe\r\n:-1\r\n*4\r\n+pubsub\r\n+noscript\r\n+loading\r\n+stale\r\n:0\r\n:0\r\n:0\r\n*6\r\n$4\r\nxack\r\n:-4\r\n*2\r\n+write\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$10\r\npfselftest\r\n:1\r\n*1\r\n+admin\r\n:0\r\n:0\r\n:0\r\n*6\r\n$6\r\nsubstr\r\n:4\r\n*1\r\n+readonly\r\n:1\r\n:1\r\n:1\r\n*6\r\n$8\r\nsmembers\r\n:2\r\n*2\r\n+readonly\r\n+sort_for_script\r\n:1\r\n:1\r\n:1\r\n*6\r\n$11\r\nunsubscribe\r\n:-1\r\n*4\r\n+pubsub\r\n+noscript\r\n+loading\r\n+stale\r\n:0\r\n:0\r\n:0\r\n*6\r\n$11\r\nzinterstore\r\n:-4\r\n*3\r\n+write\r\n+denyoom\r\n+movablekeys\r\n:0\r\n:0\r\n:0\r\n*6\r\n$6\r\nstrlen\r\n:2\r\n*2\r\n+readonly\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$7\r\npfmerge\r\n:-2\r\n*2\r\n+write\r\n+denyoom\r\n:1\r\n:-1\r\n:1\r\n*6\r\n$9\r\nrandomkey\r\n:1\r\n*2\r\n+readonly\r\n+random\r\n:0\r\n:0\r\n:0\r\n*6\r\n$6\r\nlolwut\r\n:-1\r\n*1\r\n+readonly\r\n:0\r\n:0\r\n:0\r\n*6\r\n$4\r\nrpop\r\n:2\r\n*2\r\n+write\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$5\r\nhkeys\r\n:2\r\n*2\r\n+readonly\r\n+sort_for_script\r\n:1\r\n:1\r\n:1\r\n*6\r\n$6\r\nclient\r\n:-2\r\n*2\r\n+admin\r\n+noscript\r\n:0\r\n:0\r\n:0\r\n*6\r\n$6\r\nmodule\r\n:-2\r\n*2\r\n+admin\r\n+noscript\r\n:0\r\n:0\r\n:0\r\n*6\r\n$7\r\nslowlog\r\n:-2\r\n*2\r\n+admin\r\n+random\r\n:0\r\n:0\r\n:0\r\n*6\r\n$7\r\ngeohash\r\n:-2\r\n*1\r\n+readonly\r\n:1\r\n:1\r\n:1\r\n*6\r\n$6\r\nlrange\r\n:4\r\n*1\r\n+readonly\r\n:1\r\n:1\r\n:1\r\n*6\r\n$4\r\nping\r\n:-1\r\n*2\r\n+stale\r\n+fast\r\n:0\r\n:0\r\n:0\r\n*6\r\n$8\r\nbitcount\r\n:-2\r\n*1\r\n+readonly\r\n:1\r\n:1\r\n:1\r\n*6\r\n$6\r\npubsub\r\n:-2\r\n*4\r\n+pubsub\r\n+random\r\n+loading\r\n+stale\r\n:0\r\n:0\r\n:0\r\n*6\r\n$4\r\nrole\r\n:1\r\n*3\r\n+noscript\r\n+loading\r\n+stale\r\n:0\r\n:0\r\n:0\r\n*6\r\n$4\r\nhget\r\n:3\r\n*2\r\n+readonly\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$6\r\nobject\r\n:-2\r\n*2\r\n+readonly\r\n+random\r\n:2\r\n:2\r\n:1\r\n*6\r\n$9\r\nzrevrange\r\n:-4\r\n*1\r\n+readonly\r\n:1\r\n:1\r\n:1\r\n*6\r\n$7\r\nhincrby\r\n:4\r\n*3\r\n+write\r\n+denyoom\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$9\r\nzlexcount\r\n:4\r\n*2\r\n+readonly\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$5\r\nscard\r\n:2\r\n*2\r\n+readonly\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$6\r\nappend\r\n:3\r\n*2\r\n+write\r\n+denyoom\r\n:1\r\n:1\r\n:1\r\n*6\r\n$7\r\nhstrlen\r\n:3\r\n*2\r\n+readonly\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$6\r\nconfig\r\n:-2\r\n*4\r\n+admin\r\n+noscript\r\n+loading\r\n+stale\r\n:0\r\n:0\r\n:0\r\n*6\r\n$4\r\nhset\r\n:-4\r\n*3\r\n+write\r\n+denyoom\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$16\r\nzrevrangebyscore\r\n:-4\r\n*1\r\n+readonly\r\n:1\r\n:1\r\n:1\r\n*6\r\n$4\r\nincr\r\n:2\r\n*3\r\n+write\r\n+denyoom\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$6\r\nsetbit\r\n:4\r\n*2\r\n+write\r\n+denyoom\r\n:1\r\n:1\r\n:1\r\n*6\r\n$9\r\nrpoplpush\r\n:3\r\n*2\r\n+write\r\n+denyoom\r\n:1\r\n:2\r\n:1\r\n*6\r\n$6\r\nxclaim\r\n:-6\r\n*3\r\n+write\r\n+random\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$11\r\nsinterstore\r\n:-3\r\n*2\r\n+write\r\n+denyoom\r\n:1\r\n:-1\r\n:1\r\n*6\r\n$7\r\npublish\r\n:3\r\n*4\r\n+pubsub\r\n+loading\r\n+stale\r\n+fast\r\n:0\r\n:0\r\n:0\r\n*6\r\n$5\r\nhscan\r\n:-3\r\n*2\r\n+readonly\r\n+random\r\n:1\r\n:1\r\n:1\r\n*6\r\n$5\r\nmulti\r\n:1\r\n*2\r\n+noscript\r\n+fast\r\n:0\r\n:0\r\n:0\r\n*6\r\n$3\r\nset\r\n:-3\r\n*2\r\n+write\r\n+denyoom\r\n:1\r\n:1\r\n:1\r\n*6\r\n$6\r\nlpushx\r\n:-3\r\n*3\r\n+write\r\n+denyoom\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$16\r\nzremrangebyscore\r\n:4\r\n*1\r\n+write\r\n:1\r\n:1\r\n:1\r\n*6\r\n$9\r\npexpireat\r\n:3\r\n*2\r\n+write\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$4\r\nhdel\r\n:-3\r\n*2\r\n+write\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$12\r\nbgrewriteaof\r\n:1\r\n*2\r\n+admin\r\n+noscript\r\n:0\r\n:0\r\n:0\r\n*6\r\n$7\r\nmigrate\r\n:-6\r\n*3\r\n+write\r\n+random\r\n+movablekeys\r\n:0\r\n:0\r\n:0\r\n*6\r\n$9\r\nreplicaof\r\n:3\r\n*3\r\n+admin\r\n+noscript\r\n+stale\r\n:0\r\n:0\r\n:0\r\n*6\r\n$5\r\ntouch\r\n:-2\r\n*2\r\n+readonly\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$6\r\nxsetid\r\n:3\r\n*3\r\n+write\r\n+denyoom\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$5\r\nbitop\r\n:-4\r\n*2\r\n+write\r\n+denyoom\r\n:2\r\n:-1\r\n:1\r\n*6\r\n$6\r\nswapdb\r\n:3\r\n*2\r\n+write\r\n+fast\r\n:0\r\n:0\r\n:0\r\n*6\r\n$5\r\nsdiff\r\n:-2\r\n*2\r\n+readonly\r\n+sort_for_script\r\n:1\r\n:-1\r\n:1\r\n*6\r\n$6\r\nlindex\r\n:3\r\n*1\r\n+readonly\r\n:1\r\n:1\r\n:1\r\n*6\r\n$4\r\nwait\r\n:3\r\n*1\r\n+noscript\r\n:0\r\n:0\r\n:0\r\n*6\r\n$4\r\nlrem\r\n:4\r\n*1\r\n+write\r\n:1\r\n:1\r\n:1\r\n*6\r\n$6\r\nhsetnx\r\n:4\r\n*3\r\n+write\r\n+denyoom\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$8\r\ngetrange\r\n:4\r\n*1\r\n+readonly\r\n:1\r\n:1\r\n:1\r\n*6\r\n$4\r\nhlen\r\n:2\r\n*2\r\n+readonly\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$4\r\npost\r\n:-1\r\n*2\r\n+loading\r\n+stale\r\n:0\r\n:0\r\n:0\r\n*6\r\n$9\r\nsismember\r\n:3\r\n*2\r\n+readonly\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$7\r\nunwatch\r\n:1\r\n*2\r\n+noscript\r\n+fast\r\n:0\r\n:0\r\n:0\r\n*6\r\n$5\r\nlpush\r\n:-3\r\n*3\r\n+write\r\n+denyoom\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$4\r\nscan\r\n:-2\r\n*2\r\n+readonly\r\n+random\r\n:0\r\n:0\r\n:0\r\n*6\r\n$5\r\nsmove\r\n:4\r\n*2\r\n+write\r\n+fast\r\n:1\r\n:2\r\n:1\r\n*6\r\n$7\r\ncluster\r\n:-2\r\n*1\r\n+admin\r\n:0\r\n:0\r\n:0\r\n*6\r\n$6\r\nbgsave\r\n:-1\r\n*2\r\n+admin\r\n+noscript\r\n:0\r\n:0\r\n:0\r\n*6\r\n$4\r\ndump\r\n:2\r\n*2\r\n+readonly\r\n+random\r\n:1\r\n:1\r\n:1\r\n*6\r\n$7\r\nlatency\r\n:-2\r\n*4\r\n+admin\r\n+noscript\r\n+loading\r\n+stale\r\n:0\r\n:0\r\n:0\r\n*6\r\n$8\r\nbzpopmin\r\n:-3\r\n*3\r\n+write\r\n+noscript\r\n+fast\r\n:1\r\n:-2\r\n:1\r\n*6\r\n$6\r\ngetbit\r\n:3\r\n*2\r\n+readonly\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$7\r\nhgetall\r\n:2\r\n*2\r\n+readonly\r\n+random\r\n:1\r\n:1\r\n:1\r\n*6\r\n$6\r\nrename\r\n:3\r\n*1\r\n+write\r\n:1\r\n:2\r\n:1\r\n*6\r\n$9\r\nsubscribe\r\n:-2\r\n*4\r\n+pubsub\r\n+noscript\r\n+loading\r\n+stale\r\n:0\r\n:0\r\n:0\r\n*6\r\n$4\r\nxdel\r\n:-3\r\n*2\r\n+write\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$15\r\nzremrangebyrank\r\n:4\r\n*1\r\n+write\r\n:1\r\n:1\r\n:1\r\n*6\r\n$4\r\ntype\r\n:2\r\n*2\r\n+readonly\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$6\r\nscript\r\n:-2\r\n*1\r\n+noscript\r\n:0\r\n:0\r\n:0\r\n*6\r\n$5\r\nhmset\r\n:-4\r\n*3\r\n+write\r\n+denyoom\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$6\r\nsunion\r\n:-2\r\n*2\r\n+readonly\r\n+sort_for_script\r\n:1\r\n:-1\r\n:1\r\n*6\r\n$4\r\nmget\r\n:-2\r\n*2\r\n+readonly\r\n+fast\r\n:1\r\n:-1\r\n:1\r\n*6\r\n$10\r\nbrpoplpush\r\n:4\r\n*3\r\n+write\r\n+denyoom\r\n+noscript\r\n:1\r\n:2\r\n:1\r\n*6\r\n$6\r\ngeoadd\r\n:-5\r\n*2\r\n+write\r\n+denyoom\r\n:1\r\n:1\r\n:1\r\n*6\r\n$6\r\ndecrby\r\n:3\r\n*3\r\n+write\r\n+denyoom\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$4\r\necho\r\n:2\r\n*1\r\n+fast\r\n:0\r\n:0\r\n:0\r\n*6\r\n$6\r\ndbsize\r\n:1\r\n*2\r\n+readonly\r\n+fast\r\n:0\r\n:0\r\n:0\r\n*6\r\n$5\r\nzcard\r\n:2\r\n*2\r\n+readonly\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$6\r\nselect\r\n:2\r\n*2\r\n+loading\r\n+fast\r\n:0\r\n:0\r\n:0\r\n*6\r\n$4\r\nsadd\r\n:-3\r\n*3\r\n+write\r\n+denyoom\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$5\r\nhost:\r\n:-1\r\n*2\r\n+loading\r\n+stale\r\n:0\r\n:0\r\n:0\r\n*6\r\n$5\r\nsscan\r\n:-3\r\n*2\r\n+readonly\r\n+random\r\n:1\r\n:1\r\n:1\r\n*6\r\n$12\r\ngeoradius_ro\r\n:-6\r\n*2\r\n+readonly\r\n+movablekeys\r\n:1\r\n:1\r\n:1\r\n*6\r\n$7\r\nmonitor\r\n:1\r\n*2\r\n+admin\r\n+noscript\r\n:0\r\n:0\r\n:0\r\n*6\r\n$14\r\nzremrangebylex\r\n:4\r\n*1\r\n+write\r\n:1\r\n:1\r\n:1\r\n*6\r\n$11\r\nsunionstore\r\n:-3\r\n*2\r\n+write\r\n+denyoom\r\n:1\r\n:-1\r\n:1\r\n*6\r\n$5\r\nzscan\r\n:-3\r\n*2\r\n+readonly\r\n+random\r\n:1\r\n:1\r\n:1\r\n*6\r\n$9\r\nreadwrite\r\n:1\r\n*1\r\n+fast\r\n:0\r\n:0\r\n:0\r\n*6\r\n$6\r\nxgroup\r\n:-2\r\n*2\r\n+write\r\n+denyoom\r\n:2\r\n:2\r\n:1\r\n*6\r\n$5\r\nsetex\r\n:4\r\n*2\r\n+write\r\n+denyoom\r\n:1\r\n:1\r\n:1\r\n*6\r\n$4\r\nsave\r\n:1\r\n*2\r\n+admin\r\n+noscript\r\n:0\r\n:0\r\n:0\r\n*6\r\n$5\r\nhvals\r\n:2\r\n*2\r\n+readonly\r\n+sort_for_script\r\n:1\r\n:1\r\n:1\r\n*6\r\n$5\r\nwatch\r\n:-2\r\n*2\r\n+noscript\r\n+fast\r\n:1\r\n:-1\r\n:1\r\n*6\r\n$7\r\nhexists\r\n:3\r\n*2\r\n+readonly\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$4\r\ninfo\r\n:-1\r\n*3\r\n+random\r\n+loading\r\n+stale\r\n:0\r\n:0\r\n:0\r\n*6\r\n$5\r\npsync\r\n:3\r\n*3\r\n+readonly\r\n+admin\r\n+noscript\r\n:0\r\n:0\r\n:0\r\n*6\r\n$11\r\nzrangebylex\r\n:-4\r\n*1\r\n+readonly\r\n:1\r\n:1\r\n:1\r\n*6\r\n$4\r\nzadd\r\n:-4\r\n*3\r\n+write\r\n+denyoom\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$4\r\nxlen\r\n:2\r\n*2\r\n+readonly\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$4\r\nauth\r\n:2\r\n*4\r\n+noscript\r\n+loading\r\n+stale\r\n+fast\r\n:0\r\n:0\r\n:0\r\n*6\r\n$4\r\nsrem\r\n:-3\r\n*2\r\n+write\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$9\r\ngeoradius\r\n:-6\r\n*2\r\n+write\r\n+movablekeys\r\n:1\r\n:1\r\n:1\r\n*6\r\n$4\r\nexec\r\n:1\r\n*2\r\n+noscript\r\n+skip_monitor\r\n:0\r\n:0\r\n:0\r\n*6\r\n$7\r\npfcount\r\n:-2\r\n*1\r\n+readonly\r\n:1\r\n:-1\r\n:1\r\n*6\r\n$7\r\nzpopmin\r\n:-2\r\n*2\r\n+write\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$4\r\nmove\r\n:3\r\n*2\r\n+write\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$5\r\nxtrim\r\n:-2\r\n*3\r\n+write\r\n+random\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$6\r\nasking\r\n:1\r\n*1\r\n+fast\r\n:0\r\n:0\r\n:0\r\n*6\r\n$4\r\npttl\r\n:2\r\n*3\r\n+readonly\r\n+random\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$11\r\nsrandmember\r\n:-2\r\n*2\r\n+readonly\r\n+random\r\n:1\r\n:1\r\n:1\r\n*6\r\n$8\r\nflushall\r\n:-1\r\n*1\r\n+write\r\n:0\r\n:0\r\n:0\r\n*6\r\n$4\r\nsort\r\n:-2\r\n*3\r\n+write\r\n+denyoom\r\n+movablekeys\r\n:1\r\n:1\r\n:1\r\n*6\r\n$3\r\ndel\r\n:-2\r\n*1\r\n+write\r\n:1\r\n:-1\r\n:1\r\n*6\r\n$14\r\nrestore-asking\r\n:-4\r\n*3\r\n+write\r\n+denyoom\r\n+asking\r\n:1\r\n:1\r\n:1\r\n*6\r\n$10\r\npsubscribe\r\n:-2\r\n*4\r\n+pubsub\r\n+noscript\r\n+loading\r\n+stale\r\n:0\r\n:0\r\n:0\r\n*6\r\n$4\r\ndecr\r\n:2\r\n*3\r\n+write\r\n+denyoom\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$6\r\nincrby\r\n:3\r\n*3\r\n+write\r\n+denyoom\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$14\r\nzrevrangebylex\r\n:-4\r\n*1\r\n+readonly\r\n:1\r\n:1\r\n:1\r\n*6\r\n$8\r\nbitfield\r\n:-2\r\n*2\r\n+write\r\n+denyoom\r\n:1\r\n:1\r\n:1\r\n*6\r\n$6\r\nexists\r\n:-2\r\n*2\r\n+readonly\r\n+fast\r\n:1\r\n:-1\r\n:1\r\n*6\r\n$8\r\nreplconf\r\n:-1\r\n*4\r\n+admin\r\n+noscript\r\n+loading\r\n+stale\r\n:0\r\n:0\r\n:0\r\n*6\r\n$7\r\nzincrby\r\n:4\r\n*3\r\n+write\r\n+denyoom\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$5\r\nblpop\r\n:-3\r\n*2\r\n+write\r\n+noscript\r\n:1\r\n:-2\r\n:1\r\n*6\r\n$4\r\nlpop\r\n:2\r\n*2\r\n+write\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$3\r\nttl\r\n:2\r\n*3\r\n+readonly\r\n+random\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$5\r\nxread\r\n:-4\r\n*3\r\n+readonly\r\n+noscript\r\n+movablekeys\r\n:1\r\n:1\r\n:1\r\n*6\r\n$5\r\nrpush\r\n:-3\r\n*3\r\n+write\r\n+denyoom\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$8\r\nzrevrank\r\n:3\r\n*2\r\n+readonly\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$11\r\nincrbyfloat\r\n:3\r\n*3\r\n+write\r\n+denyoom\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$5\r\nbrpop\r\n:-3\r\n*2\r\n+write\r\n+noscript\r\n:1\r\n:-2\r\n:1\r\n*6\r\n$4\r\nxadd\r\n:-5\r\n*4\r\n+write\r\n+denyoom\r\n+random\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$8\r\nsetrange\r\n:4\r\n*2\r\n+write\r\n+denyoom\r\n:1\r\n:1\r\n:1\r\n*6\r\n$17\r\ngeoradiusbymember\r\n:-5\r\n*2\r\n+write\r\n+movablekeys\r\n:1\r\n:1\r\n:1\r\n*6\r\n$6\r\nunlink\r\n:-2\r\n*2\r\n+write\r\n+fast\r\n:1\r\n:-1\r\n:1\r\n*6\r\n$8\r\nexpireat\r\n:3\r\n*2\r\n+write\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$5\r\ndebug\r\n:-2\r\n*2\r\n+admin\r\n+noscript\r\n:0\r\n:0\r\n:0\r\n*6\r\n$20\r\ngeoradiusbymember_ro\r\n:-5\r\n*2\r\n+readonly\r\n+movablekeys\r\n:1\r\n:1\r\n:1\r\n*6\r\n$4\r\nlset\r\n:4\r\n*2\r\n+write\r\n+denyoom\r\n:1\r\n:1\r\n:1\r\n*6\r\n$6\r\nzscore\r\n:3\r\n*2\r\n+readonly\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$4\r\nllen\r\n:2\r\n*2\r\n+readonly\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$4\r\ntime\r\n:1\r\n*2\r\n+random\r\n+fast\r\n:0\r\n:0\r\n:0\r\n*6\r\n$8\r\nshutdown\r\n:-1\r\n*4\r\n+admin\r\n+noscript\r\n+loading\r\n+stale\r\n:0\r\n:0\r\n:0\r\n*6\r\n$7\r\nevalsha\r\n:-3\r\n*2\r\n+noscript\r\n+movablekeys\r\n:0\r\n:0\r\n:0\r\n*6\r\n$6\r\nzcount\r\n:4\r\n*2\r\n+readonly\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$6\r\nmemory\r\n:-2\r\n*2\r\n+readonly\r\n+random\r\n:0\r\n:0\r\n:0\r\n*6\r\n$5\r\nxinfo\r\n:-2\r\n*2\r\n+readonly\r\n+random\r\n:2\r\n:2\r\n:1\r\n*6\r\n$8\r\nxpending\r\n:-3\r\n*2\r\n+readonly\r\n+random\r\n:1\r\n:1\r\n:1\r\n*6\r\n$4\r\neval\r\n:-3\r\n*2\r\n+noscript\r\n+movablekeys\r\n:0\r\n:0\r\n:0\r\n*6\r\n$6\r\nxrange\r\n:-4\r\n*1\r\n+readonly\r\n:1\r\n:1\r\n:1\r\n*6\r\n$7\r\nrestore\r\n:-4\r\n*2\r\n+write\r\n+denyoom\r\n:1\r\n:1\r\n:1\r\n*6\r\n$7\r\nzpopmax\r\n:-2\r\n*2\r\n+write\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$4\r\nmset\r\n:-3\r\n*2\r\n+write\r\n+denyoom\r\n:1\r\n:-1\r\n:2\r\n*6\r\n$4\r\nspop\r\n:-2\r\n*3\r\n+write\r\n+random\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$5\r\nltrim\r\n:4\r\n*1\r\n+write\r\n:1\r\n:1\r\n:1\r\n*6\r\n$5\r\nzrank\r\n:3\r\n*2\r\n+readonly\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$9\r\nxrevrange\r\n:-4\r\n*1\r\n+readonly\r\n:1\r\n:1\r\n:1\r\n*6\r\n$3\r\nget\r\n:2\r\n*2\r\n+readonly\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$7\r\nflushdb\r\n:-1\r\n*1\r\n+write\r\n:0\r\n:0\r\n:0\r\n*6\r\n$5\r\nhmget\r\n:-3\r\n*2\r\n+readonly\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$6\r\nmsetnx\r\n:-3\r\n*2\r\n+write\r\n+denyoom\r\n:1\r\n:-1\r\n:2\r\n*6\r\n$7\r\npersist\r\n:2\r\n*2\r\n+write\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$11\r\nzunionstore\r\n:-4\r\n*3\r\n+write\r\n+denyoom\r\n+movablekeys\r\n:0\r\n:0\r\n:0\r\n*6\r\n$7\r\ncommand\r\n:0\r\n*3\r\n+random\r\n+loading\r\n+stale\r\n:0\r\n:0\r\n:0\r\n*6\r\n$8\r\nrenamenx\r\n:3\r\n*2\r\n+write\r\n+fast\r\n:1\r\n:2\r\n:1\r\n*6\r\n$6\r\nzrange\r\n:-4\r\n*1\r\n+readonly\r\n:1\r\n:1\r\n:1\r\n*6\r\n$7\r\npexpire\r\n:3\r\n*2\r\n+write\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$4\r\nkeys\r\n:2\r\n*2\r\n+readonly\r\n+sort_for_script\r\n:0\r\n:0\r\n:0\r\n*6\r\n$4\r\nzrem\r\n:-3\r\n*2\r\n+write\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$5\r\npfadd\r\n:-2\r\n*3\r\n+write\r\n+denyoom\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$6\r\npsetex\r\n:4\r\n*2\r\n+write\r\n+denyoom\r\n:1\r\n:1\r\n:1\r\n*6\r\n$13\r\nzrangebyscore\r\n:-4\r\n*1\r\n+readonly\r\n:1\r\n:1\r\n:1\r\n*6\r\n$4\r\nsync\r\n:1\r\n*3\r\n+readonly\r\n+admin\r\n+noscript\r\n:0\r\n:0\r\n:0\r\n*6\r\n$7\r\npfdebug\r\n:-3\r\n*1\r\n+write\r\n:0\r\n:0\r\n:0\r\n*6\r\n$7\r\ndiscard\r\n:1\r\n*2\r\n+noscript\r\n+fast\r\n:0\r\n:0\r\n:0\r\n*6\r\n$8\r\nreadonly\r\n:1\r\n*1\r\n+fast\r\n:0\r\n:0\r\n:0\r\n*6\r\n$7\r\ngeodist\r\n:-4\r\n*1\r\n+readonly\r\n:1\r\n:1\r\n:1\r\n*6\r\n$6\r\ngeopos\r\n:-2\r\n*1\r\n+readonly\r\n:1\r\n:1\r\n:1\r\n*6\r\n$6\r\nbitpos\r\n:-3\r\n*1\r\n+readonly\r\n:1\r\n:1\r\n:1\r\n*6\r\n$6\r\nsinter\r\n:-2\r\n*2\r\n+readonly\r\n+sort_for_script\r\n:1\r\n:-1\r\n:1\r\n*6\r\n$6\r\ngetset\r\n:3\r\n*2\r\n+write\r\n+denyoom\r\n:1\r\n:1\r\n:1\r\n*6\r\n$7\r\nslaveof\r\n:3\r\n*3\r\n+admin\r\n+noscript\r\n+stale\r\n:0\r\n:0\r\n:0\r\n*6\r\n$6\r\nrpushx\r\n:-3\r\n*3\r\n+write\r\n+denyoom\r\n+fast\r\n:1\r\n:1\r\n:1\r\n*6\r\n$7\r\nlinsert\r\n:5\r\n*2\r\n+write\r\n+denyoom\r\n:1\r\n:1\r\n:1\r\n*6\r\n$6\r\nexpire\r\n:3\r\n*2\r\n+write\r\n+fast\r\n:1\r\n:1\r\n:1\r\n" + res := ` +*200 +*6 +$12 +hincrbyfloat +:4 +*3 ++write ++denyoom ++fast +:1 +:1 +:1 +*6 +$10 +xreadgroup +:-7 +*3 ++write ++noscript ++movablekeys +:1 +:1 +:1 +*6 +$10 +sdiffstore +:-3 +*2 ++write ++denyoom +:1 +:-1 +:1 +*6 +$8 +lastsave +:1 +*2 ++random ++fast +:0 +:0 +:0 +*6 +$5 +setnx +:3 +*3 ++write ++denyoom ++fast +:1 +:1 +:1 +*6 +$8 +bzpopmax +:-3 +*3 ++write ++noscript ++fast +:1 +:-2 +:1 +*6 +$12 +punsubscribe +:-1 +*4 ++pubsub ++noscript ++loading ++stale +:0 +:0 +:0 +*6 +$4 +xack +:-4 +*2 ++write ++fast +:1 +:1 +:1 +*6 +$10 +pfselftest +:1 +*1 ++admin +:0 +:0 +:0 +*6 +$6 +substr +:4 +*1 ++readonly +:1 +:1 +:1 +*6 +$8 +smembers +:2 +*2 ++readonly ++sort_for_script +:1 +:1 +:1 +*6 +$11 +unsubscribe +:-1 +*4 ++pubsub ++noscript ++loading ++stale +:0 +:0 +:0 +*6 +$11 +zinterstore +:-4 +*3 ++write ++denyoom ++movablekeys +:0 +:0 +:0 +*6 +$6 +strlen +:2 +*2 ++readonly ++fast +:1 +:1 +:1 +*6 +$7 +pfmerge +:-2 +*2 ++write ++denyoom +:1 +:-1 +:1 +*6 +$9 +randomkey +:1 +*2 ++readonly ++random +:0 +:0 +:0 +*6 +$6 +lolwut +:-1 +*1 ++readonly +:0 +:0 +:0 +*6 +$4 +rpop +:2 +*2 ++write ++fast +:1 +:1 +:1 +*6 +$5 +hkeys +:2 +*2 ++readonly ++sort_for_script +:1 +:1 +:1 +*6 +$6 +client +:-2 +*2 ++admin ++noscript +:0 +:0 +:0 +*6 +$6 +module +:-2 +*2 ++admin ++noscript +:0 +:0 +:0 +*6 +$7 +slowlog +:-2 +*2 ++admin ++random +:0 +:0 +:0 +*6 +$7 +geohash +:-2 +*1 ++readonly +:1 +:1 +:1 +*6 +$6 +lrange +:4 +*1 ++readonly +:1 +:1 +:1 +*6 +$4 +ping +:-1 +*2 ++stale ++fast +:0 +:0 +:0 +*6 +$8 +bitcount +:-2 +*1 ++readonly +:1 +:1 +:1 +*6 +$6 +pubsub +:-2 +*4 ++pubsub ++random ++loading ++stale +:0 +:0 +:0 +*6 +$4 +role +:1 +*3 ++noscript ++loading ++stale +:0 +:0 +:0 +*6 +$4 +hget +:3 +*2 ++readonly ++fast +:1 +:1 +:1 +*6 +$6 +object +:-2 +*2 ++readonly ++random +:2 +:2 +:1 +*6 +$9 +zrevrange +:-4 +*1 ++readonly +:1 +:1 +:1 +*6 +$7 +hincrby +:4 +*3 ++write ++denyoom ++fast +:1 +:1 +:1 +*6 +$9 +zlexcount +:4 +*2 ++readonly ++fast +:1 +:1 +:1 +*6 +$5 +scard +:2 +*2 ++readonly ++fast +:1 +:1 +:1 +*6 +$6 +append +:3 +*2 ++write ++denyoom +:1 +:1 +:1 +*6 +$7 +hstrlen +:3 +*2 ++readonly ++fast +:1 +:1 +:1 +*6 +$6 +config +:-2 +*4 ++admin ++noscript ++loading ++stale +:0 +:0 +:0 +*6 +$4 +hset +:-4 +*3 ++write ++denyoom ++fast +:1 +:1 +:1 +*6 +$16 +zrevrangebyscore +:-4 +*1 ++readonly +:1 +:1 +:1 +*6 +$4 +incr +:2 +*3 ++write ++denyoom ++fast +:1 +:1 +:1 +*6 +$6 +setbit +:4 +*2 ++write ++denyoom +:1 +:1 +:1 +*6 +$9 +rpoplpush +:3 +*2 ++write ++denyoom +:1 +:2 +:1 +*6 +$6 +xclaim +:-6 +*3 ++write ++random ++fast +:1 +:1 +:1 +*6 +$11 +sinterstore +:-3 +*2 ++write ++denyoom +:1 +:-1 +:1 +*6 +$7 +publish +:3 +*4 ++pubsub ++loading ++stale ++fast +:0 +:0 +:0 +*6 +$5 +hscan +:-3 +*2 ++readonly ++random +:1 +:1 +:1 +*6 +$5 +multi +:1 +*2 ++noscript ++fast +:0 +:0 +:0 +*6 +$3 +set +:-3 +*2 ++write ++denyoom +:1 +:1 +:1 +*6 +$6 +lpushx +:-3 +*3 ++write ++denyoom ++fast +:1 +:1 +:1 +*6 +$16 +zremrangebyscore +:4 +*1 ++write +:1 +:1 +:1 +*6 +$9 +pexpireat +:3 +*2 ++write ++fast +:1 +:1 +:1 +*6 +$4 +hdel +:-3 +*2 ++write ++fast +:1 +:1 +:1 +*6 +$12 +bgrewriteaof +:1 +*2 ++admin ++noscript +:0 +:0 +:0 +*6 +$7 +migrate +:-6 +*3 ++write ++random ++movablekeys +:0 +:0 +:0 +*6 +$9 +replicaof +:3 +*3 ++admin ++noscript ++stale +:0 +:0 +:0 +*6 +$5 +touch +:-2 +*2 ++readonly ++fast +:1 +:1 +:1 +*6 +$6 +xsetid +:3 +*3 ++write ++denyoom ++fast +:1 +:1 +:1 +*6 +$5 +bitop +:-4 +*2 ++write ++denyoom +:2 +:-1 +:1 +*6 +$6 +swapdb +:3 +*2 ++write ++fast +:0 +:0 +:0 +*6 +$5 +sdiff +:-2 +*2 ++readonly ++sort_for_script +:1 +:-1 +:1 +*6 +$6 +lindex +:3 +*1 ++readonly +:1 +:1 +:1 +*6 +$4 +wait +:3 +*1 ++noscript +:0 +:0 +:0 +*6 +$4 +lrem +:4 +*1 ++write +:1 +:1 +:1 +*6 +$6 +hsetnx +:4 +*3 ++write ++denyoom ++fast +:1 +:1 +:1 +*6 +$8 +getrange +:4 +*1 ++readonly +:1 +:1 +:1 +*6 +$4 +hlen +:2 +*2 ++readonly ++fast +:1 +:1 +:1 +*6 +$4 +post +:-1 +*2 ++loading ++stale +:0 +:0 +:0 +*6 +$9 +sismember +:3 +*2 ++readonly ++fast +:1 +:1 +:1 +*6 +$7 +unwatch +:1 +*2 ++noscript ++fast +:0 +:0 +:0 +*6 +$5 +lpush +:-3 +*3 ++write ++denyoom ++fast +:1 +:1 +:1 +*6 +$4 +scan +:-2 +*2 ++readonly ++random +:0 +:0 +:0 +*6 +$5 +smove +:4 +*2 ++write ++fast +:1 +:2 +:1 +*6 +$7 +cluster +:-2 +*1 ++admin +:0 +:0 +:0 +*6 +$6 +bgsave +:-1 +*2 ++admin ++noscript +:0 +:0 +:0 +*6 +$4 +dump +:2 +*2 ++readonly ++random +:1 +:1 +:1 +*6 +$7 +latency +:-2 +*4 ++admin ++noscript ++loading ++stale +:0 +:0 +:0 +*6 +$8 +bzpopmin +:-3 +*3 ++write ++noscript ++fast +:1 +:-2 +:1 +*6 +$6 +getbit +:3 +*2 ++readonly ++fast +:1 +:1 +:1 +*6 +$7 +hgetall +:2 +*2 ++readonly ++random +:1 +:1 +:1 +*6 +$6 +rename +:3 +*1 ++write +:1 +:2 +:1 +*6 +$9 +subscribe +:-2 +*4 ++pubsub ++noscript ++loading ++stale +:0 +:0 +:0 +*6 +$4 +xdel +:-3 +*2 ++write ++fast +:1 +:1 +:1 +*6 +$15 +zremrangebyrank +:4 +*1 ++write +:1 +:1 +:1 +*6 +$4 +type +:2 +*2 ++readonly ++fast +:1 +:1 +:1 +*6 +$6 +script +:-2 +*1 ++noscript +:0 +:0 +:0 +*6 +$5 +hmset +:-4 +*3 ++write ++denyoom ++fast +:1 +:1 +:1 +*6 +$6 +sunion +:-2 +*2 ++readonly ++sort_for_script +:1 +:-1 +:1 +*6 +$4 +mget +:-2 +*2 ++readonly ++fast +:1 +:-1 +:1 +*6 +$10 +brpoplpush +:4 +*3 ++write ++denyoom ++noscript +:1 +:2 +:1 +*6 +$6 +geoadd +:-5 +*2 ++write ++denyoom +:1 +:1 +:1 +*6 +$6 +decrby +:3 +*3 ++write ++denyoom ++fast +:1 +:1 +:1 +*6 +$4 +echo +:2 +*1 ++fast +:0 +:0 +:0 +*6 +$6 +dbsize +:1 +*2 ++readonly ++fast +:0 +:0 +:0 +*6 +$5 +zcard +:2 +*2 ++readonly ++fast +:1 +:1 +:1 +*6 +$6 +select +:2 +*2 ++loading ++fast +:0 +:0 +:0 +*6 +$4 +sadd +:-3 +*3 ++write ++denyoom ++fast +:1 +:1 +:1 +*6 +$5 +host: +:-1 +*2 ++loading ++stale +:0 +:0 +:0 +*6 +$5 +sscan +:-3 +*2 ++readonly ++random +:1 +:1 +:1 +*6 +$12 +georadius_ro +:-6 +*2 ++readonly ++movablekeys +:1 +:1 +:1 +*6 +$7 +monitor +:1 +*2 ++admin ++noscript +:0 +:0 +:0 +*6 +$14 +zremrangebylex +:4 +*1 ++write +:1 +:1 +:1 +*6 +$11 +sunionstore +:-3 +*2 ++write ++denyoom +:1 +:-1 +:1 +*6 +$5 +zscan +:-3 +*2 ++readonly ++random +:1 +:1 +:1 +*6 +$9 +readwrite +:1 +*1 ++fast +:0 +:0 +:0 +*6 +$6 +xgroup +:-2 +*2 ++write ++denyoom +:2 +:2 +:1 +*6 +$5 +setex +:4 +*2 ++write ++denyoom +:1 +:1 +:1 +*6 +$4 +save +:1 +*2 ++admin ++noscript +:0 +:0 +:0 +*6 +$5 +hvals +:2 +*2 ++readonly ++sort_for_script +:1 +:1 +:1 +*6 +$5 +watch +:-2 +*2 ++noscript ++fast +:1 +:-1 +:1 +*6 +$7 +hexists +:3 +*2 ++readonly ++fast +:1 +:1 +:1 +*6 +$4 +info +:-1 +*3 ++random ++loading ++stale +:0 +:0 +:0 +*6 +$5 +psync +:3 +*3 ++readonly ++admin ++noscript +:0 +:0 +:0 +*6 +$11 +zrangebylex +:-4 +*1 ++readonly +:1 +:1 +:1 +*6 +$4 +zadd +:-4 +*3 ++write ++denyoom ++fast +:1 +:1 +:1 +*6 +$4 +xlen +:2 +*2 ++readonly ++fast +:1 +:1 +:1 +*6 +$4 +auth +:2 +*4 ++noscript ++loading ++stale ++fast +:0 +:0 +:0 +*6 +$4 +srem +:-3 +*2 ++write ++fast +:1 +:1 +:1 +*6 +$9 +georadius +:-6 +*2 ++write ++movablekeys +:1 +:1 +:1 +*6 +$4 +exec +:1 +*2 ++noscript ++skip_monitor +:0 +:0 +:0 +*6 +$7 +pfcount +:-2 +*1 ++readonly +:1 +:-1 +:1 +*6 +$7 +zpopmin +:-2 +*2 ++write ++fast +:1 +:1 +:1 +*6 +$4 +move +:3 +*2 ++write ++fast +:1 +:1 +:1 +*6 +$5 +xtrim +:-2 +*3 ++write ++random ++fast +:1 +:1 +:1 +*6 +$6 +asking +:1 +*1 ++fast +:0 +:0 +:0 +*6 +$4 +pttl +:2 +*3 ++readonly ++random ++fast +:1 +:1 +:1 +*6 +$11 +srandmember +:-2 +*2 ++readonly ++random +:1 +:1 +:1 +*6 +$8 +flushall +:-1 +*1 ++write +:0 +:0 +:0 +*6 +$4 +sort +:-2 +*3 ++write ++denyoom ++movablekeys +:1 +:1 +:1 +*6 +$3 +del +:-2 +*1 ++write +:1 +:-1 +:1 +*6 +$14 +restore-asking +:-4 +*3 ++write ++denyoom ++asking +:1 +:1 +:1 +*6 +$10 +psubscribe +:-2 +*4 ++pubsub ++noscript ++loading ++stale +:0 +:0 +:0 +*6 +$4 +decr +:2 +*3 ++write ++denyoom ++fast +:1 +:1 +:1 +*6 +$6 +incrby +:3 +*3 ++write ++denyoom ++fast +:1 +:1 +:1 +*6 +$14 +zrevrangebylex +:-4 +*1 ++readonly +:1 +:1 +:1 +*6 +$8 +bitfield +:-2 +*2 ++write ++denyoom +:1 +:1 +:1 +*6 +$6 +exists +:-2 +*2 ++readonly ++fast +:1 +:-1 +:1 +*6 +$8 +replconf +:-1 +*4 ++admin ++noscript ++loading ++stale +:0 +:0 +:0 +*6 +$7 +zincrby +:4 +*3 ++write ++denyoom ++fast +:1 +:1 +:1 +*6 +$5 +blpop +:-3 +*2 ++write ++noscript +:1 +:-2 +:1 +*6 +$4 +lpop +:2 +*2 ++write ++fast +:1 +:1 +:1 +*6 +$3 +ttl +:2 +*3 ++readonly ++random ++fast +:1 +:1 +:1 +*6 +$5 +xread +:-4 +*3 ++readonly ++noscript ++movablekeys +:1 +:1 +:1 +*6 +$5 +rpush +:-3 +*3 ++write ++denyoom ++fast +:1 +:1 +:1 +*6 +$8 +zrevrank +:3 +*2 ++readonly ++fast +:1 +:1 +:1 +*6 +$11 +incrbyfloat +:3 +*3 ++write ++denyoom ++fast +:1 +:1 +:1 +*6 +$5 +brpop +:-3 +*2 ++write ++noscript +:1 +:-2 +:1 +*6 +$4 +xadd +:-5 +*4 ++write ++denyoom ++random ++fast +:1 +:1 +:1 +*6 +$8 +setrange +:4 +*2 ++write ++denyoom +:1 +:1 +:1 +*6 +$17 +georadiusbymember +:-5 +*2 ++write ++movablekeys +:1 +:1 +:1 +*6 +$6 +unlink +:-2 +*2 ++write ++fast +:1 +:-1 +:1 +*6 +$8 +expireat +:3 +*2 ++write ++fast +:1 +:1 +:1 +*6 +$5 +debug +:-2 +*2 ++admin ++noscript +:0 +:0 +:0 +*6 +$20 +georadiusbymember_ro +:-5 +*2 ++readonly ++movablekeys +:1 +:1 +:1 +*6 +$4 +lset +:4 +*2 ++write ++denyoom +:1 +:1 +:1 +*6 +$6 +zscore +:3 +*2 ++readonly ++fast +:1 +:1 +:1 +*6 +$4 +llen +:2 +*2 ++readonly ++fast +:1 +:1 +:1 +*6 +$4 +time +:1 +*2 ++random ++fast +:0 +:0 +:0 +*6 +$8 +shutdown +:-1 +*4 ++admin ++noscript ++loading ++stale +:0 +:0 +:0 +*6 +$7 +evalsha +:-3 +*2 ++noscript ++movablekeys +:0 +:0 +:0 +*6 +$6 +zcount +:4 +*2 ++readonly ++fast +:1 +:1 +:1 +*6 +$6 +memory +:-2 +*2 ++readonly ++random +:0 +:0 +:0 +*6 +$5 +xinfo +:-2 +*2 ++readonly ++random +:2 +:2 +:1 +*6 +$8 +xpending +:-3 +*2 ++readonly ++random +:1 +:1 +:1 +*6 +$4 +eval +:-3 +*2 ++noscript ++movablekeys +:0 +:0 +:0 +*6 +$6 +xrange +:-4 +*1 ++readonly +:1 +:1 +:1 +*6 +$7 +restore +:-4 +*2 ++write ++denyoom +:1 +:1 +:1 +*6 +$7 +zpopmax +:-2 +*2 ++write ++fast +:1 +:1 +:1 +*6 +$4 +mset +:-3 +*2 ++write ++denyoom +:1 +:-1 +:2 +*6 +$4 +spop +:-2 +*3 ++write ++random ++fast +:1 +:1 +:1 +*6 +$5 +ltrim +:4 +*1 ++write +:1 +:1 +:1 +*6 +$5 +zrank +:3 +*2 ++readonly ++fast +:1 +:1 +:1 +*6 +$9 +xrevrange +:-4 +*1 ++readonly +:1 +:1 +:1 +*6 +$3 +get +:2 +*2 ++readonly ++fast +:1 +:1 +:1 +*6 +$7 +flushdb +:-1 +*1 ++write +:0 +:0 +:0 +*6 +$5 +hmget +:-3 +*2 ++readonly ++fast +:1 +:1 +:1 +*6 +$6 +msetnx +:-3 +*2 ++write ++denyoom +:1 +:-1 +:2 +*6 +$7 +persist +:2 +*2 ++write ++fast +:1 +:1 +:1 +*6 +$11 +zunionstore +:-4 +*3 ++write ++denyoom ++movablekeys +:0 +:0 +:0 +*6 +$7 +command +:0 +*3 ++random ++loading ++stale +:0 +:0 +:0 +*6 +$8 +renamenx +:3 +*2 ++write ++fast +:1 +:2 +:1 +*6 +$6 +zrange +:-4 +*1 ++readonly +:1 +:1 +:1 +*6 +$7 +pexpire +:3 +*2 ++write ++fast +:1 +:1 +:1 +*6 +$4 +keys +:2 +*2 ++readonly ++sort_for_script +:0 +:0 +:0 +*6 +$4 +zrem +:-3 +*2 ++write ++fast +:1 +:1 +:1 +*6 +$5 +pfadd +:-2 +*3 ++write ++denyoom ++fast +:1 +:1 +:1 +*6 +$6 +psetex +:4 +*2 ++write ++denyoom +:1 +:1 +:1 +*6 +$13 +zrangebyscore +:-4 +*1 ++readonly +:1 +:1 +:1 +*6 +$4 +sync +:1 +*3 ++readonly ++admin ++noscript +:0 +:0 +:0 +*6 +$7 +pfdebug +:-3 +*1 ++write +:0 +:0 +:0 +*6 +$7 +discard +:1 +*2 ++noscript ++fast +:0 +:0 +:0 +*6 +$8 +readonly +:1 +*1 ++fast +:0 +:0 +:0 +*6 +$7 +geodist +:-4 +*1 ++readonly +:1 +:1 +:1 +*6 +$6 +geopos +:-2 +*1 ++readonly +:1 +:1 +:1 +*6 +$6 +bitpos +:-3 +*1 ++readonly +:1 +:1 +:1 +*6 +$6 +sinter +:-2 +*2 ++readonly ++sort_for_script +:1 +:-1 +:1 +*6 +$6 +getset +:3 +*2 ++write ++denyoom +:1 +:1 +:1 +*6 +$7 +slaveof +:3 +*3 ++admin ++noscript ++stale +:0 +:0 +:0 +*6 +$6 +rpushx +:-3 +*3 ++write ++denyoom ++fast +:1 +:1 +:1 +*6 +$7 +linsert +:5 +*2 ++write ++denyoom +:1 +:1 +:1 +*6 +$6 +expire +:3 +*2 ++write ++fast +:1 +:1 +:1 + ` - c.WriteRaw(res) + c.WriteBulk(res) } diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_connection.go b/vendor/github.com/alicebob/miniredis/v2/cmd_connection.go index defbbccab9..cca060a721 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_connection.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_connection.go @@ -4,7 +4,6 @@ package miniredis import ( "fmt" - "strconv" "strings" "github.com/alicebob/miniredis/v2/server" @@ -75,23 +74,29 @@ func (m *Miniredis) cmdAuth(c *server.Peer, cmd string, args []string) { c.WriteError(msgNotFromScripts) return } - username := "default" - pw := args[0] + + var opts = struct { + username string + password string + }{ + username: "default", + password: args[0], + } if len(args) == 2 { - username, pw = args[0], args[1] + opts.username, opts.password = args[0], args[1] } withTx(m, c, func(c *server.Peer, ctx *connCtx) { - if len(m.passwords) == 0 && username == "default" { + if len(m.passwords) == 0 && opts.username == "default" { c.WriteError("ERR AUTH called without any password configured for the default user. Are you sure your configuration is correct?") return } - setPW, ok := m.passwords[username] + setPW, ok := m.passwords[opts.username] if !ok { c.WriteError("WRONGPASS invalid username-password pair") return } - if setPW != pw { + if setPW != opts.password { c.WriteError("WRONGPASS invalid username-password pair") return } @@ -109,17 +114,16 @@ func (m *Miniredis) cmdHello(c *server.Peer, cmd string, args []string) { } var opts struct { - version int - username, password string + version int + username string + password string } - versionArg, args := args[0], args[1:] - var err error - opts.version, err = strconv.Atoi(versionArg) - if err != nil { - c.WriteError("ERR Protocol version is not an integer or out of range") + if ok := optIntErr(c, args[0], &opts.version, "ERR Protocol version is not an integer or out of range"); !ok { return } + args = args[1:] + switch opts.version { case 2, 3: default: @@ -199,8 +203,9 @@ func (m *Miniredis) cmdEcho(c *server.Peer, cmd string, args []string) { return } + msg := args[0] + withTx(m, c, func(c *server.Peer, ctx *connCtx) { - msg := args[0] c.WriteBulk(msg) }) } @@ -212,27 +217,25 @@ func (m *Miniredis) cmdSelect(c *server.Peer, cmd string, args []string) { c.WriteError(errWrongNumber(cmd)) return } - if !m.handleAuth(c) { + if !m.isValidCMD(c, cmd) { return } - if m.checkPubsub(c, cmd) { + + var opts struct { + id int + } + if ok := optInt(c, args[0], &opts.id); !ok { return } withTx(m, c, func(c *server.Peer, ctx *connCtx) { - id, err := strconv.Atoi(args[0]) - if err != nil { - c.WriteError(msgInvalidInt) - setDirty(c) - return - } - if id < 0 { + if opts.id < 0 { c.WriteError(msgDBIndexOutOfRange) setDirty(c) return } - ctx.selectedDB = id + ctx.selectedDB = opts.id c.WriteOK() }) } @@ -248,26 +251,26 @@ func (m *Miniredis) cmdSwapdb(c *server.Peer, cmd string, args []string) { return } + var opts struct { + id1 int + id2 int + } + + if ok := optIntErr(c, args[0], &opts.id1, "ERR invalid first DB index"); !ok { + return + } + if ok := optIntErr(c, args[1], &opts.id2, "ERR invalid second DB index"); !ok { + return + } + withTx(m, c, func(c *server.Peer, ctx *connCtx) { - id1, err := strconv.Atoi(args[0]) - if err != nil { - c.WriteError("ERR invalid first DB index") - setDirty(c) - return - } - id2, err := strconv.Atoi(args[1]) - if err != nil { - c.WriteError("ERR invalid second DB index") - setDirty(c) - return - } - if id1 < 0 || id2 < 0 { + if opts.id1 < 0 || opts.id2 < 0 { c.WriteError(msgDBIndexOutOfRange) setDirty(c) return } - m.swapDB(id1, id2) + m.swapDB(opts.id1, opts.id2) c.WriteOK() }) diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_generic.go b/vendor/github.com/alicebob/miniredis/v2/cmd_generic.go index f9f06bfb96..76d6b0a8ca 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_generic.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_generic.go @@ -13,8 +13,8 @@ import ( // commandsGeneric handles EXPIRE, TTL, PERSIST, &c. func commandsGeneric(m *Miniredis) { + m.srv.Register("COPY", m.cmdCopy) m.srv.Register("DEL", m.cmdDel) - m.srv.Register("UNLINK", m.cmdDel) // DUMP m.srv.Register("EXISTS", m.cmdExists) m.srv.Register("EXPIRE", makeCmdExpire(m, false, time.Second)) @@ -31,12 +31,12 @@ func commandsGeneric(m *Miniredis) { m.srv.Register("RENAME", m.cmdRename) m.srv.Register("RENAMENX", m.cmdRenamenx) // RESTORE - // SORT m.srv.Register("TOUCH", m.cmdTouch) m.srv.Register("TTL", m.cmdTTL) m.srv.Register("TYPE", m.cmdType) m.srv.Register("SCAN", m.cmdScan) - m.srv.Register("COPY", m.cmdCopy) + // SORT + m.srv.Register("UNLINK", m.cmdDel) } // generic expire command for EXPIRE, PEXPIRE, EXPIREAT, PEXPIREAT @@ -56,12 +56,12 @@ func makeCmdExpire(m *Miniredis, unix bool, d time.Duration) func(*server.Peer, return } - key := args[0] - value := args[1] - i, err := strconv.Atoi(value) - if err != nil { - setDirty(c) - c.WriteError(msgInvalidInt) + var opts struct { + key string + value int + } + opts.key = args[0] + if ok := optInt(c, args[1], &opts.value); !ok { return } @@ -69,17 +69,17 @@ func makeCmdExpire(m *Miniredis, unix bool, d time.Duration) func(*server.Peer, db := m.db(ctx.selectedDB) // Key must be present. - if _, ok := db.keys[key]; !ok { + if _, ok := db.keys[opts.key]; !ok { c.WriteInt(0) return } if unix { - db.ttl[key] = m.at(i, d) + db.ttl[opts.key] = m.at(opts.value, d) } else { - db.ttl[key] = time.Duration(i) * d + db.ttl[opts.key] = time.Duration(opts.value) * d } - db.keyVersion[key]++ - db.checkTTL(key) + db.keyVersion[opts.key]++ + db.checkTTL(opts.key) c.WriteInt(1) }) } @@ -318,21 +318,23 @@ func (m *Miniredis) cmdMove(c *server.Peer, cmd string, args []string) { return } - key := args[0] - targetDB, err := strconv.Atoi(args[1]) - if err != nil { - targetDB = 0 + var opts struct { + key string + targetDB int } + opts.key = args[0] + opts.targetDB, _ = strconv.Atoi(args[1]) + withTx(m, c, func(c *server.Peer, ctx *connCtx) { - if ctx.selectedDB == targetDB { + if ctx.selectedDB == opts.targetDB { c.WriteError("ERR source and destination objects are the same") return } db := m.db(ctx.selectedDB) - targetDB := m.db(targetDB) + targetDB := m.db(opts.targetDB) - if !db.move(key, targetDB) { + if !db.move(opts.key, targetDB) { c.WriteInt(0) return } @@ -413,17 +415,23 @@ func (m *Miniredis) cmdRename(c *server.Peer, cmd string, args []string) { return } - from, to := args[0], args[1] + opts := struct { + from string + to string + }{ + from: args[0], + to: args[1], + } withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) - if !db.exists(from) { + if !db.exists(opts.from) { c.WriteError(msgKeyNotFound) return } - db.rename(from, to) + db.rename(opts.from, opts.to) c.WriteOK() }) } @@ -442,22 +450,28 @@ func (m *Miniredis) cmdRenamenx(c *server.Peer, cmd string, args []string) { return } - from, to := args[0], args[1] + opts := struct { + from string + to string + }{ + from: args[0], + to: args[1], + } withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) - if !db.exists(from) { + if !db.exists(opts.from) { c.WriteError(msgKeyNotFound) return } - if db.exists(to) { + if db.exists(opts.to) { c.WriteInt(0) return } - db.rename(from, to) + db.rename(opts.from, opts.to) c.WriteInt(1) }) } @@ -476,22 +490,20 @@ func (m *Miniredis) cmdScan(c *server.Peer, cmd string, args []string) { return } - cursor, err := strconv.Atoi(args[0]) - if err != nil { - setDirty(c) - c.WriteError(msgInvalidCursor) - return - } - args = args[1:] - - // MATCH, COUNT and TYPE options - var ( + var opts struct { + cursor int withMatch bool match string withType bool _type string - ) + } + + if ok := optIntErr(c, args[0], &opts.cursor, msgInvalidCursor); !ok { + return + } + args = args[1:] + // MATCH, COUNT and TYPE options for len(args) > 0 { if strings.ToLower(args[0]) == "count" { // we do nothing with count @@ -514,8 +526,8 @@ func (m *Miniredis) cmdScan(c *server.Peer, cmd string, args []string) { c.WriteError(msgSyntaxError) return } - withMatch = true - match, args = args[1], args[2:] + opts.withMatch = true + opts.match, args = args[1], args[2:] continue } if strings.ToLower(args[0]) == "type" { @@ -524,8 +536,8 @@ func (m *Miniredis) cmdScan(c *server.Peer, cmd string, args []string) { c.WriteError(msgSyntaxError) return } - withType = true - _type, args = strings.ToLower(args[1]), args[2:] + opts.withType = true + opts._type, args = strings.ToLower(args[1]), args[2:] continue } setDirty(c) @@ -537,7 +549,7 @@ func (m *Miniredis) cmdScan(c *server.Peer, cmd string, args []string) { db := m.db(ctx.selectedDB) // We return _all_ (matched) keys every time. - if cursor != 0 { + if opts.cursor != 0 { // Invalid cursor. c.WriteLen(2) c.WriteBulk("0") // no next cursor @@ -547,11 +559,11 @@ func (m *Miniredis) cmdScan(c *server.Peer, cmd string, args []string) { var keys []string - if withType { + if opts.withType { keys = make([]string, 0) for k, t := range db.keys { // type must be given exactly; no pattern matching is performed - if t == _type { + if t == opts._type { keys = append(keys, k) } } @@ -560,8 +572,8 @@ func (m *Miniredis) cmdScan(c *server.Peer, cmd string, args []string) { keys = db.allKeys() } - if withMatch { - keys, _ = matchKeys(keys, match) + if opts.withMatch { + keys, _ = matchKeys(keys, opts.match) } c.WriteLen(2) diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_geo.go b/vendor/github.com/alicebob/miniredis/v2/cmd_geo.go index a6c1901d68..29a92d550f 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_geo.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_geo.go @@ -226,28 +226,28 @@ func (m *Miniredis) cmdGeoradius(c *server.Peer, cmd string, args []string) { } args = args[5:] - var ( - withDist = false - withCoord = false - direction = unsorted - count = 0 - withStore = false - storeKey = "" - withStoredist = false - storedistKey = "" - ) + var opts struct { + withDist bool + withCoord bool + direction direction // unsorted + count int + withStore bool + storeKey string + withStoredist bool + storedistKey string + } for len(args) > 0 { arg := args[0] args = args[1:] switch strings.ToUpper(arg) { case "WITHCOORD": - withCoord = true + opts.withCoord = true case "WITHDIST": - withDist = true + opts.withDist = true case "ASC": - direction = asc + opts.direction = asc case "DESC": - direction = desc + opts.direction = desc case "COUNT": if len(args) == 0 { setDirty(c) @@ -266,15 +266,15 @@ func (m *Miniredis) cmdGeoradius(c *server.Peer, cmd string, args []string) { return } args = args[1:] - count = n + opts.count = n case "STORE": if len(args) == 0 { setDirty(c) c.WriteError("ERR syntax error") return } - withStore = true - storeKey = args[0] + opts.withStore = true + opts.storeKey = args[0] args = args[1:] case "STOREDIST": if len(args) == 0 { @@ -282,8 +282,8 @@ func (m *Miniredis) cmdGeoradius(c *server.Peer, cmd string, args []string) { c.WriteError("ERR syntax error") return } - withStoredist = true - storedistKey = args[0] + opts.withStoredist = true + opts.storedistKey = args[0] args = args[1:] default: setDirty(c) @@ -292,14 +292,14 @@ func (m *Miniredis) cmdGeoradius(c *server.Peer, cmd string, args []string) { } } - if strings.ToUpper(cmd) == "GEORADIUS_RO" && (withStore || withStoredist) { + if strings.ToUpper(cmd) == "GEORADIUS_RO" && (opts.withStore || opts.withStoredist) { setDirty(c) c.WriteError("ERR syntax error") return } withTx(m, c, func(c *server.Peer, ctx *connCtx) { - if (withStore || withStoredist) && (withDist || withCoord) { + if (opts.withStore || opts.withStoredist) && (opts.withDist || opts.withCoord) { c.WriteError("ERR STORE option in GEORADIUS is not compatible with WITHDIST, WITHHASH and WITHCOORDS options") return } @@ -310,9 +310,9 @@ func (m *Miniredis) cmdGeoradius(c *server.Peer, cmd string, args []string) { matches := withinRadius(members, longitude, latitude, radius*toMeter) // deal with ASC/DESC - if direction != unsorted { + if opts.direction != unsorted { sort.Slice(matches, func(i, j int) bool { - if direction == desc { + if opts.direction == desc { return matches[i].Distance > matches[j].Distance } return matches[i].Distance < matches[j].Distance @@ -320,25 +320,25 @@ func (m *Miniredis) cmdGeoradius(c *server.Peer, cmd string, args []string) { } // deal with COUNT - if count > 0 && len(matches) > count { - matches = matches[:count] + if opts.count > 0 && len(matches) > opts.count { + matches = matches[:opts.count] } // deal with "STORE x" - if withStore { - db.del(storeKey, true) + if opts.withStore { + db.del(opts.storeKey, true) for _, member := range matches { - db.ssetAdd(storeKey, member.Score, member.Name) + db.ssetAdd(opts.storeKey, member.Score, member.Name) } c.WriteInt(len(matches)) return } // deal with "STOREDIST x" - if withStoredist { - db.del(storedistKey, true) + if opts.withStoredist { + db.del(opts.storedistKey, true) for _, member := range matches { - db.ssetAdd(storedistKey, member.Distance/toMeter, member.Name) + db.ssetAdd(opts.storedistKey, member.Distance/toMeter, member.Name) } c.WriteInt(len(matches)) return @@ -346,24 +346,24 @@ func (m *Miniredis) cmdGeoradius(c *server.Peer, cmd string, args []string) { c.WriteLen(len(matches)) for _, member := range matches { - if !withDist && !withCoord { + if !opts.withDist && !opts.withCoord { c.WriteBulk(member.Name) continue } len := 1 - if withDist { + if opts.withDist { len++ } - if withCoord { + if opts.withCoord { len++ } c.WriteLen(len) c.WriteBulk(member.Name) - if withDist { + if opts.withDist { c.WriteBulk(fmt.Sprintf("%.4f", member.Distance/toMeter)) } - if withCoord { + if opts.withCoord { c.WriteLen(2) c.WriteBulk(fmt.Sprintf("%f", member.Longitude)) c.WriteBulk(fmt.Sprintf("%f", member.Latitude)) @@ -386,45 +386,53 @@ func (m *Miniredis) cmdGeoradiusbymember(c *server.Peer, cmd string, args []stri return } - key := args[0] - member := args[1] + opts := struct { + key string + member string + radius float64 + toMeter float64 + + withDist bool + withCoord bool + direction direction // unsorted + count int + withStore bool + storeKey string + withStoredist bool + storedistKey string + }{ + key: args[0], + member: args[1], + } - radius, err := strconv.ParseFloat(args[2], 64) - if err != nil || radius < 0 { + r, err := strconv.ParseFloat(args[2], 64) + if err != nil || r < 0 { setDirty(c) c.WriteError(errWrongNumber(cmd)) return } - toMeter := parseUnit(args[3]) - if toMeter == 0 { + opts.radius = r + + opts.toMeter = parseUnit(args[3]) + if opts.toMeter == 0 { setDirty(c) c.WriteError(errWrongNumber(cmd)) return } args = args[4:] - var ( - withDist = false - withCoord = false - direction = unsorted - count = 0 - withStore = false - storeKey = "" - withStoredist = false - storedistKey = "" - ) for len(args) > 0 { arg := args[0] args = args[1:] switch strings.ToUpper(arg) { case "WITHCOORD": - withCoord = true + opts.withCoord = true case "WITHDIST": - withDist = true + opts.withDist = true case "ASC": - direction = asc + opts.direction = asc case "DESC": - direction = desc + opts.direction = desc case "COUNT": if len(args) == 0 { setDirty(c) @@ -443,15 +451,15 @@ func (m *Miniredis) cmdGeoradiusbymember(c *server.Peer, cmd string, args []stri return } args = args[1:] - count = n + opts.count = n case "STORE": if len(args) == 0 { setDirty(c) c.WriteError("ERR syntax error") return } - withStore = true - storeKey = args[0] + opts.withStore = true + opts.storeKey = args[0] args = args[1:] case "STOREDIST": if len(args) == 0 { @@ -459,8 +467,8 @@ func (m *Miniredis) cmdGeoradiusbymember(c *server.Peer, cmd string, args []stri c.WriteError("ERR syntax error") return } - withStoredist = true - storedistKey = args[0] + opts.withStoredist = true + opts.storedistKey = args[0] args = args[1:] default: setDirty(c) @@ -469,44 +477,44 @@ func (m *Miniredis) cmdGeoradiusbymember(c *server.Peer, cmd string, args []stri } } - if strings.ToUpper(cmd) == "GEORADIUSBYMEMBER_RO" && (withStore || withStoredist) { + if strings.ToUpper(cmd) == "GEORADIUSBYMEMBER_RO" && (opts.withStore || opts.withStoredist) { setDirty(c) c.WriteError("ERR syntax error") return } withTx(m, c, func(c *server.Peer, ctx *connCtx) { - if (withStore || withStoredist) && (withDist || withCoord) { + if (opts.withStore || opts.withStoredist) && (opts.withDist || opts.withCoord) { c.WriteError("ERR STORE option in GEORADIUS is not compatible with WITHDIST, WITHHASH and WITHCOORDS options") return } db := m.db(ctx.selectedDB) - if !db.exists(key) { + if !db.exists(opts.key) { c.WriteNull() return } - if db.t(key) != "zset" { + if db.t(opts.key) != "zset" { c.WriteError(ErrWrongType.Error()) return } // get position of member - if !db.ssetExists(key, member) { + if !db.ssetExists(opts.key, opts.member) { c.WriteError("ERR could not decode requested zset member") return } - score := db.ssetScore(key, member) + score := db.ssetScore(opts.key, opts.member) longitude, latitude := fromGeohash(uint64(score)) - members := db.ssetElements(key) - matches := withinRadius(members, longitude, latitude, radius*toMeter) + members := db.ssetElements(opts.key) + matches := withinRadius(members, longitude, latitude, opts.radius*opts.toMeter) // deal with ASC/DESC - if direction != unsorted { + if opts.direction != unsorted { sort.Slice(matches, func(i, j int) bool { - if direction == desc { + if opts.direction == desc { return matches[i].Distance > matches[j].Distance } return matches[i].Distance < matches[j].Distance @@ -514,25 +522,25 @@ func (m *Miniredis) cmdGeoradiusbymember(c *server.Peer, cmd string, args []stri } // deal with COUNT - if count > 0 && len(matches) > count { - matches = matches[:count] + if opts.count > 0 && len(matches) > opts.count { + matches = matches[:opts.count] } // deal with "STORE x" - if withStore { - db.del(storeKey, true) + if opts.withStore { + db.del(opts.storeKey, true) for _, member := range matches { - db.ssetAdd(storeKey, member.Score, member.Name) + db.ssetAdd(opts.storeKey, member.Score, member.Name) } c.WriteInt(len(matches)) return } // deal with "STOREDIST x" - if withStoredist { - db.del(storedistKey, true) + if opts.withStoredist { + db.del(opts.storedistKey, true) for _, member := range matches { - db.ssetAdd(storedistKey, member.Distance/toMeter, member.Name) + db.ssetAdd(opts.storedistKey, member.Distance/opts.toMeter, member.Name) } c.WriteInt(len(matches)) return @@ -540,24 +548,24 @@ func (m *Miniredis) cmdGeoradiusbymember(c *server.Peer, cmd string, args []stri c.WriteLen(len(matches)) for _, member := range matches { - if !withDist && !withCoord { + if !opts.withDist && !opts.withCoord { c.WriteBulk(member.Name) continue } len := 1 - if withDist { + if opts.withDist { len++ } - if withCoord { + if opts.withCoord { len++ } c.WriteLen(len) c.WriteBulk(member.Name) - if withDist { - c.WriteBulk(fmt.Sprintf("%.4f", member.Distance/toMeter)) + if opts.withDist { + c.WriteBulk(fmt.Sprintf("%.4f", member.Distance/opts.toMeter)) } - if withCoord { + if opts.withCoord { c.WriteLen(2) c.WriteBulk(fmt.Sprintf("%f", member.Longitude)) c.WriteBulk(fmt.Sprintf("%f", member.Latitude)) diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_hash.go b/vendor/github.com/alicebob/miniredis/v2/cmd_hash.go index 142ba63e16..09fa4522f3 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_hash.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_hash.go @@ -77,27 +77,35 @@ func (m *Miniredis) cmdHsetnx(c *server.Peer, cmd string, args []string) { return } - key, field, value := args[0], args[1], args[2] + opts := struct { + key string + field string + value string + }{ + key: args[0], + field: args[1], + value: args[2], + } withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) - if t, ok := db.keys[key]; ok && t != "hash" { + if t, ok := db.keys[opts.key]; ok && t != "hash" { c.WriteError(msgWrongType) return } - if _, ok := db.hashKeys[key]; !ok { - db.hashKeys[key] = map[string]string{} - db.keys[key] = "hash" + if _, ok := db.hashKeys[opts.key]; !ok { + db.hashKeys[opts.key] = map[string]string{} + db.keys[opts.key] = "hash" } - _, ok := db.hashKeys[key][field] + _, ok := db.hashKeys[opts.key][opts.field] if ok { c.WriteInt(0) return } - db.hashKeys[key][field] = value - db.keyVersion[key]++ + db.hashKeys[opts.key][opts.field] = opts.value + db.keyVersion[opts.key]++ c.WriteInt(1) }) } @@ -191,12 +199,18 @@ func (m *Miniredis) cmdHdel(c *server.Peer, cmd string, args []string) { return } - key, fields := args[0], args[1:] + opts := struct { + key string + fields []string + }{ + key: args[0], + fields: args[1:], + } withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) - t, ok := db.keys[key] + t, ok := db.keys[opts.key] if !ok { // No key is zero deleted c.WriteInt(0) @@ -208,19 +222,19 @@ func (m *Miniredis) cmdHdel(c *server.Peer, cmd string, args []string) { } deleted := 0 - for _, f := range fields { - _, ok := db.hashKeys[key][f] + for _, f := range opts.fields { + _, ok := db.hashKeys[opts.key][f] if !ok { continue } - delete(db.hashKeys[key], f) + delete(db.hashKeys[opts.key], f) deleted++ } c.WriteInt(deleted) // Nothing left. Remove the whole key. - if len(db.hashKeys[key]) == 0 { - db.del(key, true) + if len(db.hashKeys[opts.key]) == 0 { + db.del(opts.key, true) } }) } @@ -239,12 +253,18 @@ func (m *Miniredis) cmdHexists(c *server.Peer, cmd string, args []string) { return } - key, field := args[0], args[1] + opts := struct { + key string + field string + }{ + key: args[0], + field: args[1], + } withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) - t, ok := db.keys[key] + t, ok := db.keys[opts.key] if !ok { c.WriteInt(0) return @@ -254,7 +274,7 @@ func (m *Miniredis) cmdHexists(c *server.Peer, cmd string, args []string) { return } - if _, ok := db.hashKeys[key][field]; !ok { + if _, ok := db.hashKeys[opts.key][opts.field]; !ok { c.WriteInt(0) return } @@ -494,24 +514,27 @@ func (m *Miniredis) cmdHincrby(c *server.Peer, cmd string, args []string) { return } - key, field, deltas := args[0], args[1], args[2] - - delta, err := strconv.Atoi(deltas) - if err != nil { - setDirty(c) - c.WriteError(msgInvalidInt) + opts := struct { + key string + field string + delta int + }{ + key: args[0], + field: args[1], + } + if ok := optInt(c, args[2], &opts.delta); !ok { return } withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) - if t, ok := db.keys[key]; ok && t != "hash" { + if t, ok := db.keys[opts.key]; ok && t != "hash" { c.WriteError(msgWrongType) return } - v, err := db.hashIncr(key, field, delta) + v, err := db.hashIncr(opts.key, opts.field, opts.delta) if err != nil { c.WriteError(err.Error()) return @@ -534,24 +557,31 @@ func (m *Miniredis) cmdHincrbyfloat(c *server.Peer, cmd string, args []string) { return } - key, field, deltas := args[0], args[1], args[2] - - delta, _, err := big.ParseFloat(deltas, 10, 128, 0) + opts := struct { + key string + field string + delta *big.Float + }{ + key: args[0], + field: args[1], + } + delta, _, err := big.ParseFloat(args[2], 10, 128, 0) if err != nil { setDirty(c) c.WriteError(msgInvalidFloat) return } + opts.delta = delta withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) - if t, ok := db.keys[key]; ok && t != "hash" { + if t, ok := db.keys[opts.key]; ok && t != "hash" { c.WriteError(msgWrongType) return } - v, err := db.hashIncrfloat(key, field, delta) + v, err := db.hashIncrfloat(opts.key, opts.field, opts.delta) if err != nil { c.WriteError(err.Error()) return @@ -574,18 +604,20 @@ func (m *Miniredis) cmdHscan(c *server.Peer, cmd string, args []string) { return } - key := args[0] - cursor, err := strconv.Atoi(args[1]) - if err != nil { - setDirty(c) - c.WriteError(msgInvalidCursor) + opts := struct { + key string + cursor int + withMatch bool + match string + }{ + key: args[0], + } + if ok := optIntErr(c, args[1], &opts.cursor, msgInvalidCursor); !ok { return } args = args[2:] // MATCH and COUNT options - var withMatch bool - var match string for len(args) > 0 { if strings.ToLower(args[0]) == "count" { // we do nothing with count @@ -609,8 +641,8 @@ func (m *Miniredis) cmdHscan(c *server.Peer, cmd string, args []string) { c.WriteError(msgSyntaxError) return } - withMatch = true - match, args = args[1], args[2:] + opts.withMatch = true + opts.match, args = args[1], args[2:] continue } setDirty(c) @@ -622,21 +654,21 @@ func (m *Miniredis) cmdHscan(c *server.Peer, cmd string, args []string) { db := m.db(ctx.selectedDB) // return _all_ (matched) keys every time - if cursor != 0 { + if opts.cursor != 0 { // Invalid cursor. c.WriteLen(2) c.WriteBulk("0") // no next cursor c.WriteLen(0) // no elements return } - if db.exists(key) && db.t(key) != "hash" { + if db.exists(opts.key) && db.t(opts.key) != "hash" { c.WriteError(ErrWrongType.Error()) return } - members := db.hashFields(key) - if withMatch { - members, _ = matchKeys(members, match) + members := db.hashFields(opts.key) + if opts.withMatch { + members, _ = matchKeys(members, opts.match) } c.WriteLen(2) @@ -645,7 +677,7 @@ func (m *Miniredis) cmdHscan(c *server.Peer, cmd string, args []string) { c.WriteLen(len(members) * 2) for _, k := range members { c.WriteBulk(k) - c.WriteBulk(db.hashGet(key, k)) + c.WriteBulk(db.hashGet(opts.key, k)) } }) } diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_hll.go b/vendor/github.com/alicebob/miniredis/v2/cmd_hll.go index bd2f90c838..a7b483673e 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_hll.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_hll.go @@ -52,7 +52,7 @@ func (m *Miniredis) cmdPfcount(c *server.Peer, cmd string, args []string) { return } - keys := args[:] + keys := args withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_info.go b/vendor/github.com/alicebob/miniredis/v2/cmd_info.go new file mode 100644 index 0000000000..e5984a9b2c --- /dev/null +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_info.go @@ -0,0 +1,40 @@ +package miniredis + +import ( + "fmt" + + "github.com/alicebob/miniredis/v2/server" +) + +// Command 'INFO' from https://redis.io/commands/info/ +func (m *Miniredis) cmdInfo(c *server.Peer, cmd string, args []string) { + if !m.isValidCMD(c, cmd) { + return + } + + if len(args) > 1 { + setDirty(c) + c.WriteError(errWrongNumber(cmd)) + return + } + + withTx(m, c, func(c *server.Peer, ctx *connCtx) { + const ( + clientsSectionName = "clients" + clientsSectionContent = "# Clients\nconnected_clients:%d\r\n" + ) + + var result string + + for _, key := range args { + if key != clientsSectionName { + setDirty(c) + c.WriteError(fmt.Sprintf("section (%s) is not supported", key)) + return + } + } + result = fmt.Sprintf(clientsSectionContent, m.Server().ClientsLen()) + + c.WriteBulk(result) + }) +} diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_list.go b/vendor/github.com/alicebob/miniredis/v2/cmd_list.go index 62f9691887..efd0362b4b 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_list.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_list.go @@ -23,6 +23,7 @@ func commandsList(m *Miniredis) { m.srv.Register("BRPOP", m.cmdBrpop) m.srv.Register("BRPOPLPUSH", m.cmdBrpoplpush) m.srv.Register("LINDEX", m.cmdLindex) + m.srv.Register("LPOS", m.cmdLpos) m.srv.Register("LINSERT", m.cmdLinsert) m.srv.Register("LLEN", m.cmdLlen) m.srv.Register("LPOP", m.cmdLpop) @@ -62,28 +63,23 @@ func (m *Miniredis) cmdBXpop(c *server.Peer, cmd string, args []string, lr leftr return } - timeoutS := args[len(args)-1] - keys := args[:len(args)-1] - - timeout, err := strconv.Atoi(timeoutS) - if err != nil { - setDirty(c) - c.WriteError(msgInvalidTimeout) - return + var opts struct { + keys []string + timeout time.Duration } - if timeout < 0 { - setDirty(c) - c.WriteError(msgNegTimeout) + + if ok := optDuration(c, args[len(args)-1], &opts.timeout); !ok { return } + opts.keys = args[:len(args)-1] blocking( m, c, - time.Duration(timeout)*time.Second, + opts.timeout, func(c *server.Peer, ctx *connCtx) bool { db := m.db(ctx.selectedDB) - for _, key := range keys { + for _, key := range opts.keys { if !db.exists(key) { continue } @@ -165,6 +161,153 @@ func (m *Miniredis) cmdLindex(c *server.Peer, cmd string, args []string) { }) } +// LPOS key element [RANK rank] [COUNT num-matches] [MAXLEN len] +func (m *Miniredis) cmdLpos(c *server.Peer, cmd string, args []string) { + if !m.handleAuth(c) { + return + } + if m.checkPubsub(c, cmd) { + return + } + + if len(args) == 1 { + setDirty(c) + c.WriteError(errWrongNumber(cmd)) + return + } + + // Extract options from arguments if present. + // + // Redis allows duplicate options and uses the last specified. + // `LPOS key term RANK 1 RANK 2` is effectively the same as + // `LPOS key term RANK 2` + if len(args)%2 == 1 { + setDirty(c) + c.WriteError(msgSyntaxError) + return + } + rank, count := 1, 1 // Default values + var maxlen int // Default value is the list length (see below) + var countSpecified, maxlenSpecified bool + if len(args) > 2 { + for i := 2; i < len(args); i++ { + if i%2 == 0 { + val := args[i+1] + var err error + switch strings.ToLower(args[i]) { + case "rank": + if rank, err = strconv.Atoi(val); err != nil { + setDirty(c) + c.WriteError(msgInvalidInt) + return + } + if rank == 0 { + setDirty(c) + c.WriteError(msgRankIsZero) + return + } + case "count": + countSpecified = true + if count, err = strconv.Atoi(val); err != nil || count < 0 { + setDirty(c) + c.WriteError(msgCountIsNegative) + return + } + case "maxlen": + maxlenSpecified = true + if maxlen, err = strconv.Atoi(val); err != nil || maxlen < 0 { + setDirty(c) + c.WriteError(msgMaxLengthIsNegative) + return + } + default: + setDirty(c) + c.WriteError(msgSyntaxError) + return + } + } + } + } + + withTx(m, c, func(c *server.Peer, ctx *connCtx) { + db := m.db(ctx.selectedDB) + key, element := args[0], args[1] + t, ok := db.keys[key] + if !ok { + // No such key + c.WriteNull() + return + } + if t != "list" { + c.WriteError(msgWrongType) + return + } + l := db.listKeys[key] + + // RANK cannot be zero (see above). + // If RANK is positive search forward (left to right). + // If RANK is negative search backward (right to left). + // Iterator returns true to continue iterating. + iterate := func(iterator func(i int, e string) bool) { + comparisons := len(l) + // Only use max length if specified, not zero, and less than total length. + // When max length is specified, but is zero, this means "unlimited". + if maxlenSpecified && maxlen != 0 && maxlen < len(l) { + comparisons = maxlen + } + if rank > 0 { + for i := 0; i < comparisons; i++ { + if resume := iterator(i, l[i]); !resume { + return + } + } + } else if rank < 0 { + start := len(l) - 1 + end := len(l) - comparisons + for i := start; i >= end; i-- { + if resume := iterator(i, l[i]); !resume { + return + } + } + } + } + + var currentRank, currentCount int + vals := make([]int, 0, count) + iterate(func(i int, e string) bool { + if e == element { + currentRank++ + // Only collect values only after surpassing the absolute value of rank. + if rank > 0 && currentRank < rank { + return true + } + if rank < 0 && currentRank < -rank { + return true + } + vals = append(vals, i) + currentCount++ + if currentCount == count { + return false + } + } + return true + }) + + if !countSpecified && len(vals) == 0 { + c.WriteNull() + return + } + if !countSpecified && len(vals) == 1 { + c.WriteInt(vals[0]) + return + } + c.WriteLen(len(vals)) + for _, val := range vals { + c.WriteInt(val) + } + }) +} + // LINSERT func (m *Miniredis) cmdLinsert(c *server.Peer, cmd string, args []string) { if len(args) != 4 { @@ -297,18 +440,14 @@ func (m *Miniredis) cmdXpop(c *server.Peer, cmd string, args []string, lr leftri opts.key, args = args[0], args[1:] if len(args) > 0 { - v, err := strconv.Atoi(args[0]) - if err != nil { - setDirty(c) - c.WriteError(msgInvalidInt) + if ok := optInt(c, args[0], &opts.count); !ok { return } - if v < 0 { + if opts.count < 0 { setDirty(c) c.WriteError(msgOutOfRange) return } - opts.count = v opts.withCount = true args = args[1:] } @@ -471,35 +610,35 @@ func (m *Miniredis) cmdLrange(c *server.Peer, cmd string, args []string) { return } - key := args[0] - start, err := strconv.Atoi(args[1]) - if err != nil { - setDirty(c) - c.WriteError(msgInvalidInt) + opts := struct { + key string + start int + end int + }{ + key: args[0], + } + if ok := optInt(c, args[1], &opts.start); !ok { return } - end, err := strconv.Atoi(args[2]) - if err != nil { - setDirty(c) - c.WriteError(msgInvalidInt) + if ok := optInt(c, args[2], &opts.end); !ok { return } withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) - if t, ok := db.keys[key]; ok && t != "list" { + if t, ok := db.keys[opts.key]; ok && t != "list" { c.WriteError(msgWrongType) return } - l := db.listKeys[key] + l := db.listKeys[opts.key] if len(l) == 0 { c.WriteLen(0) return } - rs, re := redisRange(len(l), start, end, false) + rs, re := redisRange(len(l), opts.start, opts.end, false) c.WriteLen(re - rs) for _, el := range l[rs:re] { c.WriteBulk(el) @@ -521,42 +660,44 @@ func (m *Miniredis) cmdLrem(c *server.Peer, cmd string, args []string) { return } - key := args[0] - count, err := strconv.Atoi(args[1]) - if err != nil { - setDirty(c) - c.WriteError(msgInvalidInt) + var opts struct { + key string + count int + value string + } + opts.key = args[0] + if ok := optInt(c, args[1], &opts.count); !ok { return } - value := args[2] + opts.value = args[2] withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) - if !db.exists(key) { + if !db.exists(opts.key) { c.WriteInt(0) return } - if db.t(key) != "list" { + if db.t(opts.key) != "list" { c.WriteError(msgWrongType) return } - l := db.listKeys[key] - if count < 0 { + l := db.listKeys[opts.key] + if opts.count < 0 { reverseSlice(l) } deleted := 0 newL := []string{} toDelete := len(l) - if count < 0 { - toDelete = -count + if opts.count < 0 { + toDelete = -opts.count } - if count > 0 { - toDelete = count + if opts.count > 0 { + toDelete = opts.count } for _, el := range l { - if el == value { + if el == opts.value { if toDelete > 0 { deleted++ toDelete-- @@ -565,14 +706,14 @@ func (m *Miniredis) cmdLrem(c *server.Peer, cmd string, args []string) { } newL = append(newL, el) } - if count < 0 { + if opts.count < 0 { reverseSlice(newL) } if len(newL) == 0 { - db.del(key, true) + db.del(opts.key, true) } else { - db.listKeys[key] = newL - db.keyVersion[key]++ + db.listKeys[opts.key] = newL + db.keyVersion[opts.key]++ } c.WriteInt(deleted) @@ -593,28 +734,31 @@ func (m *Miniredis) cmdLset(c *server.Peer, cmd string, args []string) { return } - key := args[0] - index, err := strconv.Atoi(args[1]) - if err != nil { - setDirty(c) - c.WriteError(msgInvalidInt) + var opts struct { + key string + index int + value string + } + opts.key = args[0] + if ok := optInt(c, args[1], &opts.index); !ok { return } - value := args[2] + opts.value = args[2] withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) - if !db.exists(key) { + if !db.exists(opts.key) { c.WriteError(msgKeyNotFound) return } - if db.t(key) != "list" { + if db.t(opts.key) != "list" { c.WriteError(msgWrongType) return } - l := db.listKeys[key] + l := db.listKeys[opts.key] + index := opts.index if index < 0 { index = len(l) + index } @@ -622,8 +766,8 @@ func (m *Miniredis) cmdLset(c *server.Peer, cmd string, args []string) { c.WriteError(msgOutOfRange) return } - l[index] = value - db.keyVersion[key]++ + l[index] = opts.value + db.keyVersion[opts.key]++ c.WriteOK() }) @@ -643,24 +787,24 @@ func (m *Miniredis) cmdLtrim(c *server.Peer, cmd string, args []string) { return } - key := args[0] - start, err := strconv.Atoi(args[1]) - if err != nil { - setDirty(c) - c.WriteError(msgInvalidInt) + var opts struct { + key string + start int + end int + } + + opts.key = args[0] + if ok := optInt(c, args[1], &opts.start); !ok { return } - end, err := strconv.Atoi(args[2]) - if err != nil { - setDirty(c) - c.WriteError(msgInvalidInt) + if ok := optInt(c, args[2], &opts.end); !ok { return } withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) - t, ok := db.keys[key] + t, ok := db.keys[opts.key] if !ok { c.WriteOK() return @@ -670,14 +814,14 @@ func (m *Miniredis) cmdLtrim(c *server.Peer, cmd string, args []string) { return } - l := db.listKeys[key] - rs, re := redisRange(len(l), start, end, false) + l := db.listKeys[opts.key] + rs, re := redisRange(len(l), opts.start, opts.end, false) l = l[rs:re] if len(l) == 0 { - db.del(key, true) + db.del(opts.key, true) } else { - db.listKeys[key] = l - db.keyVersion[key]++ + db.listKeys[opts.key] = l + db.keyVersion[opts.key]++ } c.WriteOK() }) @@ -730,39 +874,36 @@ func (m *Miniredis) cmdBrpoplpush(c *server.Peer, cmd string, args []string) { return } - src := args[0] - dst := args[1] - timeout, err := strconv.Atoi(args[2]) - if err != nil { - setDirty(c) - c.WriteError(msgInvalidTimeout) - return + var opts struct { + src string + dst string + timeout time.Duration } - if timeout < 0 { - setDirty(c) - c.WriteError(msgNegTimeout) + opts.src = args[0] + opts.dst = args[1] + if ok := optDuration(c, args[2], &opts.timeout); !ok { return } blocking( m, c, - time.Duration(timeout)*time.Second, + opts.timeout, func(c *server.Peer, ctx *connCtx) bool { db := m.db(ctx.selectedDB) - if !db.exists(src) { + if !db.exists(opts.src) { return false } - if db.t(src) != "list" || (db.exists(dst) && db.t(dst) != "list") { + if db.t(opts.src) != "list" || (db.exists(opts.dst) && db.t(opts.dst) != "list") { c.WriteError(msgWrongType) return true } - if len(db.listKeys[src]) == 0 { + if len(db.listKeys[opts.src]) == 0 { return false } - elem := db.listPop(src) - db.listLpush(dst, elem) + elem := db.listPop(opts.src) + db.listLpush(opts.dst, elem) c.WriteBulk(elem) return true }, @@ -787,34 +928,46 @@ func (m *Miniredis) cmdLmove(c *server.Peer, cmd string, args []string) { return } - src, dst, srcDir, dstDir := args[0], args[1], strings.ToLower(args[2]), strings.ToLower(args[3]) + opts := struct { + src string + dst string + srcDir string + dstDir string + }{ + src: args[0], + dst: args[1], + srcDir: strings.ToLower(args[2]), + dstDir: strings.ToLower(args[3]), + } withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) - if !db.exists(src) { + if !db.exists(opts.src) { c.WriteNull() return } - if db.t(src) != "list" || (db.exists(dst) && db.t(dst) != "list") { + if db.t(opts.src) != "list" || (db.exists(opts.dst) && db.t(opts.dst) != "list") { c.WriteError(msgWrongType) return } var elem string - if srcDir == "left" { - elem = db.listLpop(src) - } else if srcDir == "right" { - elem = db.listPop(src) - } else { + switch opts.srcDir { + case "left": + elem = db.listLpop(opts.src) + case "right": + elem = db.listPop(opts.src) + default: c.WriteError(msgSyntaxError) return } - if dstDir == "left" { - db.listLpush(dst, elem) - } else if dstDir == "right" { - db.listPush(dst, elem) - } else { + switch opts.dstDir { + case "left": + db.listLpush(opts.dst, elem) + case "right": + db.listPush(opts.dst, elem) + default: c.WriteError(msgSyntaxError) return } diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_server.go b/vendor/github.com/alicebob/miniredis/v2/cmd_server.go index 223651d39e..6e51727ba5 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_server.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_server.go @@ -10,9 +10,11 @@ import ( ) func commandsServer(m *Miniredis) { + m.srv.Register("COMMAND", m.cmdCommand) m.srv.Register("DBSIZE", m.cmdDbsize) m.srv.Register("FLUSHALL", m.cmdFlushall) m.srv.Register("FLUSHDB", m.cmdFlushdb) + m.srv.Register("INFO", m.cmdInfo) m.srv.Register("TIME", m.cmdTime) } @@ -100,8 +102,8 @@ func (m *Miniredis) cmdTime(c *server.Peer, cmd string, args []string) { withTx(m, c, func(c *server.Peer, ctx *connCtx) { now := m.effectiveNow() nanos := now.UnixNano() - seconds := nanos / 1000000000 - microseconds := (nanos / 1000) % 1000000 + seconds := nanos / 1_000_000_000 + microseconds := (nanos / 1_000) % 1_000_000 c.WriteLen(2) c.WriteBulk(strconv.FormatInt(seconds, 10)) diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_set.go b/vendor/github.com/alicebob/miniredis/v2/cmd_set.go index a9cdf411a0..65b3b6bfbb 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_set.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_set.go @@ -3,6 +3,7 @@ package miniredis import ( + "fmt" "strconv" "strings" @@ -609,17 +610,22 @@ func (m *Miniredis) cmdSscan(c *server.Peer, cmd string, args []string) { return } - key := args[0] - cursor, err := strconv.Atoi(args[1]) - if err != nil { - setDirty(c) - c.WriteError(msgInvalidCursor) + var opts struct { + key string + value int + cursor int + count int + withMatch bool + match string + } + + opts.key = args[0] + if ok := optIntErr(c, args[1], &opts.cursor, msgInvalidCursor); !ok { return } args = args[2:] + // MATCH and COUNT options - var withMatch bool - var match string for len(args) > 0 { if strings.ToLower(args[0]) == "count" { if len(args) < 2 { @@ -627,13 +633,18 @@ func (m *Miniredis) cmdSscan(c *server.Peer, cmd string, args []string) { c.WriteError(msgSyntaxError) return } - _, err := strconv.Atoi(args[1]) - if err != nil { + count, err := strconv.Atoi(args[1]) + if err != nil || count < 0 { setDirty(c) c.WriteError(msgInvalidInt) return } - // We do nothing with count. + if count == 0 { + setDirty(c) + c.WriteError(msgSyntaxError) + return + } + opts.count = count args = args[2:] continue } @@ -643,8 +654,8 @@ func (m *Miniredis) cmdSscan(c *server.Peer, cmd string, args []string) { c.WriteError(msgSyntaxError) return } - withMatch = true - match = args[1] + opts.withMatch = true + opts.match = args[1] args = args[2:] continue } @@ -656,29 +667,38 @@ func (m *Miniredis) cmdSscan(c *server.Peer, cmd string, args []string) { withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) // return _all_ (matched) keys every time - - if cursor != 0 { + if db.exists(opts.key) && db.t(opts.key) != "set" { + c.WriteError(ErrWrongType.Error()) + return + } + members := db.setMembers(opts.key) + if opts.withMatch { + members, _ = matchKeys(members, opts.match) + } + low := opts.cursor + high := low + opts.count + // validate high is correct + if high > len(members) || high == 0 { + high = len(members) + } + if opts.cursor > high { // invalid cursor c.WriteLen(2) c.WriteBulk("0") // no next cursor c.WriteLen(0) // no elements return } - if db.exists(key) && db.t(key) != "set" { - c.WriteError(ErrWrongType.Error()) - return - } - - members := db.setMembers(key) - if withMatch { - members, _ = matchKeys(members, match) + cursorValue := low + opts.count + if cursorValue > len(members) { + cursorValue = 0 // no next cursor } - + members = members[low:high] c.WriteLen(2) - c.WriteBulk("0") // no next cursor + c.WriteBulk(fmt.Sprintf("%d", cursorValue)) c.WriteLen(len(members)) for _, k := range members { c.WriteBulk(k) } + }) } diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_sorted_set.go b/vendor/github.com/alicebob/miniredis/v2/cmd_sorted_set.go index 75b540bafb..178fcc13e4 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_sorted_set.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_sorted_set.go @@ -55,42 +55,44 @@ func (m *Miniredis) cmdZadd(c *server.Peer, cmd string, args []string) { return } - key, args := args[0], args[1:] - var ( - nx = false - xx = false - gt = false - lt = false - ch = false - incr = false - elems = map[string]float64{} - ) + var opts struct { + key string + nx bool + xx bool + gt bool + lt bool + ch bool + incr bool + } + elems := map[string]float64{} + opts.key = args[0] + args = args[1:] outer: for len(args) > 0 { switch strings.ToUpper(args[0]) { case "NX": - nx = true + opts.nx = true args = args[1:] continue case "XX": - xx = true + opts.xx = true args = args[1:] continue case "GT": - gt = true + opts.gt = true args = args[1:] continue case "LT": - lt = true + opts.lt = true args = args[1:] continue case "CH": - ch = true + opts.ch = true args = args[1:] continue case "INCR": - incr = true + opts.incr = true args = args[1:] continue default: @@ -114,21 +116,21 @@ outer: args = args[2:] } - if xx && nx { + if opts.xx && opts.nx { setDirty(c) c.WriteError(msgXXandNX) return } - if gt && lt || - gt && nx || - lt && nx { + if opts.gt && opts.lt || + opts.gt && opts.nx || + opts.lt && opts.nx { setDirty(c) c.WriteError(msgGTLTandNX) return } - if incr && len(elems) > 1 { + if opts.incr && len(elems) > 1 { setDirty(c) c.WriteError(msgSingleElementPair) return @@ -137,22 +139,22 @@ outer: withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) - if db.exists(key) && db.t(key) != "zset" { + if db.exists(opts.key) && db.t(opts.key) != "zset" { c.WriteError(ErrWrongType.Error()) return } - if incr { + if opts.incr { for member, delta := range elems { - if nx && db.ssetExists(key, member) { + if opts.nx && db.ssetExists(opts.key, member) { c.WriteNull() return } - if xx && !db.ssetExists(key, member) { + if opts.xx && !db.ssetExists(opts.key, member) { c.WriteNull() return } - newScore := db.ssetIncrby(key, member, delta) + newScore := db.ssetIncrby(opts.key, member, delta) c.WriteFloat(newScore) } return @@ -160,23 +162,23 @@ outer: res := 0 for member, score := range elems { - if nx && db.ssetExists(key, member) { + if opts.nx && db.ssetExists(opts.key, member) { continue } - if xx && !db.ssetExists(key, member) { + if opts.xx && !db.ssetExists(opts.key, member) { continue } - old := db.ssetScore(key, member) - if gt && score <= old { + old := db.ssetScore(opts.key, member) + if opts.gt && score <= old { continue } - if lt && score >= old { + if opts.lt && score >= old { continue } - if db.ssetAdd(key, score, member) { + if db.ssetAdd(opts.key, score, member) { res++ } else { - if ch && old != score { + if opts.ch && old != score { // if 'CH' is specified, only count changed keys res++ } @@ -233,14 +235,25 @@ func (m *Miniredis) cmdZcount(c *server.Peer, cmd string, args []string) { return } - key := args[0] - min, minIncl, err := parseFloatRange(args[1]) + var ( + opts struct { + key string + min float64 + minIncl bool + max float64 + maxIncl bool + } + err error + ) + + opts.key = args[0] + opts.min, opts.minIncl, err = parseFloatRange(args[1]) if err != nil { setDirty(c) c.WriteError(msgInvalidMinMax) return } - max, maxIncl, err := parseFloatRange(args[2]) + opts.max, opts.maxIncl, err = parseFloatRange(args[2]) if err != nil { setDirty(c) c.WriteError(msgInvalidMinMax) @@ -250,18 +263,18 @@ func (m *Miniredis) cmdZcount(c *server.Peer, cmd string, args []string) { withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) - if !db.exists(key) { + if !db.exists(opts.key) { c.WriteInt(0) return } - if db.t(key) != "zset" { + if db.t(opts.key) != "zset" { c.WriteError(ErrWrongType.Error()) return } - members := db.ssetElements(key) - members = withSSRange(members, min, minIncl, max, maxIncl) + members := db.ssetElements(opts.key) + members = withSSRange(members, opts.min, opts.minIncl, opts.max, opts.maxIncl) c.WriteInt(len(members)) }) } @@ -280,23 +293,30 @@ func (m *Miniredis) cmdZincrby(c *server.Peer, cmd string, args []string) { return } - key := args[0] - delta, err := strconv.ParseFloat(args[1], 64) + var opts struct { + key string + delta float64 + member string + } + + opts.key = args[0] + d, err := strconv.ParseFloat(args[1], 64) if err != nil { setDirty(c) c.WriteError(msgInvalidFloat) return } - member := args[2] + opts.delta = d + opts.member = args[2] withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) - if db.exists(key) && db.t(key) != "zset" { + if db.exists(opts.key) && db.t(opts.key) != "zset" { c.WriteError(msgWrongType) return } - newScore := db.ssetIncrby(key, member, delta) + newScore := db.ssetIncrby(opts.key, opts.member, opts.delta) c.WriteFloat(newScore) }) } @@ -889,37 +909,37 @@ func (m *Miniredis) cmdZremrangebyrank(c *server.Peer, cmd string, args []string return } - key := args[0] - start, err := strconv.Atoi(args[1]) - if err != nil { - setDirty(c) - c.WriteError(msgInvalidInt) + var opts struct { + key string + start int + end int + } + + opts.key = args[0] + if ok := optInt(c, args[1], &opts.start); !ok { return } - end, err := strconv.Atoi(args[2]) - if err != nil { - setDirty(c) - c.WriteError(msgInvalidInt) + if ok := optInt(c, args[2], &opts.end); !ok { return } withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) - if !db.exists(key) { + if !db.exists(opts.key) { c.WriteInt(0) return } - if db.t(key) != "zset" { + if db.t(opts.key) != "zset" { c.WriteError(ErrWrongType.Error()) return } - members := db.ssetMembers(key) - rs, re := redisRange(len(members), start, end, false) + members := db.ssetMembers(opts.key) + rs, re := redisRange(len(members), opts.start, opts.end, false) for _, el := range members[rs:re] { - db.ssetRem(key, el) + db.ssetRem(opts.key, el) } c.WriteInt(re - rs) }) @@ -939,14 +959,24 @@ func (m *Miniredis) cmdZremrangebyscore(c *server.Peer, cmd string, args []strin return } - key := args[0] - min, minIncl, err := parseFloatRange(args[1]) + var ( + opts struct { + key string + min float64 + minIncl bool + max float64 + maxIncl bool + } + err error + ) + opts.key = args[0] + opts.min, opts.minIncl, err = parseFloatRange(args[1]) if err != nil { setDirty(c) c.WriteError(msgInvalidMinMax) return } - max, maxIncl, err := parseFloatRange(args[2]) + opts.max, opts.maxIncl, err = parseFloatRange(args[2]) if err != nil { setDirty(c) c.WriteError(msgInvalidMinMax) @@ -956,21 +986,21 @@ func (m *Miniredis) cmdZremrangebyscore(c *server.Peer, cmd string, args []strin withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) - if !db.exists(key) { + if !db.exists(opts.key) { c.WriteInt(0) return } - if db.t(key) != "zset" { + if db.t(opts.key) != "zset" { c.WriteError(ErrWrongType.Error()) return } - members := db.ssetElements(key) - members = withSSRange(members, min, minIncl, max, maxIncl) + members := db.ssetElements(opts.key) + members = withSSRange(members, opts.min, opts.minIncl, opts.max, opts.maxIncl) for _, el := range members { - db.ssetRem(key, el.member) + db.ssetRem(opts.key, el.member) } c.WriteInt(len(members)) }) @@ -1371,17 +1401,19 @@ func (m *Miniredis) cmdZscan(c *server.Peer, cmd string, args []string) { return } - key := args[0] - cursor, err := strconv.Atoi(args[1]) - if err != nil { - setDirty(c) - c.WriteError(msgInvalidCursor) + var opts struct { + key string + cursor int + withMatch bool + match string + } + + opts.key = args[0] + if ok := optIntErr(c, args[1], &opts.cursor, msgInvalidCursor); !ok { return } args = args[2:] // MATCH and COUNT options - var withMatch bool - var match string for len(args) > 0 { if strings.ToLower(args[0]) == "count" { if len(args) < 2 { @@ -1389,8 +1421,7 @@ func (m *Miniredis) cmdZscan(c *server.Peer, cmd string, args []string) { c.WriteError(msgSyntaxError) return } - _, err := strconv.Atoi(args[1]) - if err != nil { + if _, err := strconv.Atoi(args[1]); err != nil { setDirty(c) c.WriteError(msgInvalidInt) return @@ -1405,8 +1436,8 @@ func (m *Miniredis) cmdZscan(c *server.Peer, cmd string, args []string) { c.WriteError(msgSyntaxError) return } - withMatch = true - match = args[1] + opts.withMatch = true + opts.match = args[1] args = args[2:] continue } @@ -1418,21 +1449,21 @@ func (m *Miniredis) cmdZscan(c *server.Peer, cmd string, args []string) { withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) // Paging is not implementend, all results are returned for cursor 0. - if cursor != 0 { + if opts.cursor != 0 { // Invalid cursor. c.WriteLen(2) c.WriteBulk("0") // no next cursor c.WriteLen(0) // no elements return } - if db.exists(key) && db.t(key) != "zset" { + if db.exists(opts.key) && db.t(opts.key) != "zset" { c.WriteError(ErrWrongType.Error()) return } - members := db.ssetMembers(key) - if withMatch { - members, _ = matchKeys(members, match) + members := db.ssetMembers(opts.key) + if opts.withMatch { + members, _ = matchKeys(members, opts.match) } c.WriteLen(2) @@ -1441,7 +1472,7 @@ func (m *Miniredis) cmdZscan(c *server.Peer, cmd string, args []string) { c.WriteLen(len(members) * 2) for _, k := range members { c.WriteBulk(k) - c.WriteFloat(db.ssetScore(key, k)) + c.WriteFloat(db.ssetScore(opts.key, k)) } }) } @@ -1536,17 +1567,12 @@ func (m *Miniredis) cmdZrandmember(c *server.Peer, cmd string, args []string) { args = args[1:] if len(args) > 0 { - count := args[0] - args = args[1:] - - n, err := strconv.Atoi(count) - if err != nil { - setDirty(c) - c.WriteError(msgInvalidInt) + // can be negative + if ok := optInt(c, args[0], &opts.count); !ok { return } opts.withCount = true - opts.count = n // can be negative + args = args[1:] } if len(args) > 0 && strings.ToUpper(args[0]) == "WITHSCORES" { diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_stream.go b/vendor/github.com/alicebob/miniredis/v2/cmd_stream.go index bc0991ba50..be7067b378 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_stream.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_stream.go @@ -28,6 +28,7 @@ func commandsStream(m *Miniredis) { m.srv.Register("XPENDING", m.cmdXpending) m.srv.Register("XTRIM", m.cmdXtrim) m.srv.Register("XAUTOCLAIM", m.cmdXautoclaim) + m.srv.Register("XCLAIM", m.cmdXclaim) } // XADD @@ -168,26 +169,30 @@ func (m *Miniredis) makeCmdXrange(reverse bool) server.Cmd { return } - var ( - key = args[0] - startKey = args[1] - endKey = args[2] + opts := struct { + key string + startKey string startExclusive bool + endKey string endExclusive bool - ) - if strings.HasPrefix(startKey, "(") { - startExclusive = true - startKey = startKey[1:] - if startKey == "-" || startKey == "+" { + }{ + key: args[0], + startKey: args[1], + endKey: args[2], + } + if strings.HasPrefix(opts.startKey, "(") { + opts.startExclusive = true + opts.startKey = opts.startKey[1:] + if opts.startKey == "-" || opts.startKey == "+" { setDirty(c) c.WriteError(msgInvalidStreamID) return } } - if strings.HasPrefix(endKey, "(") { - endExclusive = true - endKey = endKey[1:] - if endKey == "-" || endKey == "+" { + if strings.HasPrefix(opts.endKey, "(") { + opts.endExclusive = true + opts.endKey = opts.endKey[1:] + if opts.endKey == "-" || opts.endKey == "+" { setDirty(c) c.WriteError(msgInvalidStreamID) return @@ -205,12 +210,12 @@ func (m *Miniredis) makeCmdXrange(reverse bool) server.Cmd { } withTx(m, c, func(c *server.Peer, ctx *connCtx) { - start, err := formatStreamRangeBound(startKey, true, reverse) + start, err := formatStreamRangeBound(opts.startKey, true, reverse) if err != nil { c.WriteError(msgInvalidStreamID) return } - end, err := formatStreamRangeBound(endKey, false, reverse) + end, err := formatStreamRangeBound(opts.endKey, false, reverse) if err != nil { c.WriteError(msgInvalidStreamID) return @@ -223,17 +228,17 @@ func (m *Miniredis) makeCmdXrange(reverse bool) server.Cmd { db := m.db(ctx.selectedDB) - if !db.exists(key) { + if !db.exists(opts.key) { c.WriteLen(0) return } - if db.t(key) != "stream" { + if db.t(opts.key) != "stream" { c.WriteError(ErrWrongType.Error()) return } - var entries = db.streamKeys[key].entries + var entries = db.streamKeys[opts.key].entries if reverse { entries = reversedStreamEntries(entries) } @@ -270,11 +275,11 @@ func (m *Miniredis) makeCmdXrange(reverse bool) server.Cmd { } // Continue if start exclusive and entry ID == start - if startExclusive && streamCmp(entry.ID, start) == 0 { + if opts.startExclusive && streamCmp(entry.ID, start) == 0 { continue } // Continue if end exclusive and entry ID == end - if endExclusive && streamCmp(entry.ID, end) == 0 { + if opts.endExclusive && streamCmp(entry.ID, end) == 0 { continue } @@ -296,19 +301,44 @@ func (m *Miniredis) makeCmdXrange(reverse bool) server.Cmd { // XGROUP func (m *Miniredis) cmdXgroup(c *server.Peer, cmd string, args []string) { - if (len(args) == 4 || len(args) == 5) && strings.ToUpper(args[0]) == "CREATE" { + if len(args) == 0 { + setDirty(c) + c.WriteError(errWrongNumber(cmd)) + return + } + + subCmd, args := strings.ToUpper(args[0]), args[1:] + switch subCmd { + case "CREATE": m.cmdXgroupCreate(c, cmd, args) - } else { - j := strings.Join(args, " ") - err := fmt.Sprintf("ERR 'XGROUP %s' not supported", j) + case "DESTROY": + m.cmdXgroupDestroy(c, cmd, args) + case "CREATECONSUMER": + m.cmdXgroupCreateconsumer(c, cmd, args) + case "DELCONSUMER": + m.cmdXgroupDelconsumer(c, cmd, args) + case "HELP", + "SETID": + err := fmt.Sprintf("ERR 'XGROUP %s' not supported", subCmd) setDirty(c) c.WriteError(err) + default: + setDirty(c) + c.WriteError(fmt.Sprintf( + "ERR Unknown subcommand or wrong number of arguments for '%s'. Try XGROUP HELP.", + subCmd, + )) } } // XGROUP CREATE func (m *Miniredis) cmdXgroupCreate(c *server.Peer, cmd string, args []string) { - stream, group, id := args[1], args[2], args[3] + if len(args) != 3 && len(args) != 4 { + setDirty(c) + c.WriteError(errWrongNumber("CREATE")) + return + } + stream, group, id := args[0], args[1], args[2] withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) @@ -318,7 +348,7 @@ func (m *Miniredis) cmdXgroupCreate(c *server.Peer, cmd string, args []string) { c.WriteError(err.Error()) return } - if s == nil && len(args) == 5 && strings.ToUpper(args[4]) == "MKSTREAM" { + if s == nil && len(args) == 4 && strings.ToUpper(args[3]) == "MKSTREAM" { if s, err = db.newStream(stream); err != nil { c.WriteError(err.Error()) return @@ -338,6 +368,124 @@ func (m *Miniredis) cmdXgroupCreate(c *server.Peer, cmd string, args []string) { }) } +// XGROUP DESTROY +func (m *Miniredis) cmdXgroupDestroy(c *server.Peer, cmd string, args []string) { + if len(args) != 2 { + setDirty(c) + c.WriteError(errWrongNumber("DESTROY")) + return + } + stream, groupName := args[0], args[1] + + withTx(m, c, func(c *server.Peer, ctx *connCtx) { + db := m.db(ctx.selectedDB) + + s, err := db.stream(stream) + if err != nil { + c.WriteError(err.Error()) + return + } + if s == nil { + c.WriteError(msgXgroupKeyNotFound) + return + } + + if _, ok := s.groups[groupName]; !ok { + c.WriteInt(0) + return + } + delete(s.groups, groupName) + c.WriteInt(1) + }) +} + +// XGROUP CREATECONSUMER +func (m *Miniredis) cmdXgroupCreateconsumer(c *server.Peer, cmd string, args []string) { + if len(args) != 3 { + setDirty(c) + c.WriteError(errWrongNumber("CREATECONSUMER")) + return + } + key, groupName, consumerName := args[0], args[1], args[2] + + withTx(m, c, func(c *server.Peer, ctx *connCtx) { + db := m.db(ctx.selectedDB) + + s, err := db.stream(key) + if err != nil { + c.WriteError(err.Error()) + return + } + if s == nil { + c.WriteError(msgXgroupKeyNotFound) + return + } + + g, ok := s.groups[groupName] + if !ok { + err := fmt.Sprintf("NOGROUP No such consumer group '%s' for key name '%s'", groupName, key) + c.WriteError(err) + return + } + + if _, ok = g.consumers[consumerName]; ok { + c.WriteInt(0) + return + } + g.consumers[consumerName] = &consumer{} + c.WriteInt(1) + }) +} + +// XGROUP DELCONSUMER +func (m *Miniredis) cmdXgroupDelconsumer(c *server.Peer, cmd string, args []string) { + if len(args) != 3 { + setDirty(c) + c.WriteError(errWrongNumber("DELCONSUMER")) + return + } + key, groupName, consumerName := args[0], args[1], args[2] + + withTx(m, c, func(c *server.Peer, ctx *connCtx) { + db := m.db(ctx.selectedDB) + + s, err := db.stream(key) + if err != nil { + c.WriteError(err.Error()) + return + } + if s == nil { + c.WriteError(msgXgroupKeyNotFound) + return + } + + g, ok := s.groups[groupName] + if !ok { + err := fmt.Sprintf("NOGROUP No such consumer group '%s' for key name '%s'", groupName, key) + c.WriteError(err) + return + } + + consumer, ok := g.consumers[consumerName] + if !ok { + c.WriteInt(0) + return + } + defer delete(g.consumers, consumerName) + + if consumer.numPendingEntries > 0 { + newPending := make([]pendingEntry, 0) + for _, entry := range g.pending { + if entry.consumer != consumerName { + newPending = append(newPending, entry) + } + } + g.pending = newPending + } + c.WriteInt(consumer.numPendingEntries) + }) +} + // XINFO func (m *Miniredis) cmdXinfo(c *server.Peer, cmd string, args []string) { if len(args) < 1 { @@ -349,7 +497,11 @@ func (m *Miniredis) cmdXinfo(c *server.Peer, cmd string, args []string) { switch subCmd { case "STREAM": m.cmdXinfoStream(c, args) - case "CONSUMERS", "GROUPS", "HELP": + case "CONSUMERS": + m.cmdXinfoConsumers(c, args) + case "GROUPS": + m.cmdXinfoGroups(c, args) + case "HELP": err := fmt.Sprintf("'XINFO %s' not supported", strings.Join(args, " ")) setDirty(c) c.WriteError(err) @@ -360,7 +512,6 @@ func (m *Miniredis) cmdXinfo(c *server.Peer, cmd string, args []string) { subCmd, )) } - } // XINFO STREAM @@ -368,10 +519,11 @@ func (m *Miniredis) cmdXinfo(c *server.Peer, cmd string, args []string) { func (m *Miniredis) cmdXinfoStream(c *server.Peer, args []string) { if len(args) < 1 { setDirty(c) - c.WriteError(errWrongNumber("XINFO")) + c.WriteError(errWrongNumber("STREAM")) return } key := args[0] + withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) @@ -391,6 +543,100 @@ func (m *Miniredis) cmdXinfoStream(c *server.Peer, args []string) { }) } +// XINFO GROUPS +func (m *Miniredis) cmdXinfoGroups(c *server.Peer, args []string) { + if len(args) != 1 { + setDirty(c) + c.WriteError(errWrongNumber("GROUPS")) + return + } + key := args[0] + + withTx(m, c, func(c *server.Peer, ctx *connCtx) { + db := m.db(ctx.selectedDB) + + s, err := db.stream(key) + if err != nil { + c.WriteError(err.Error()) + return + } + if s == nil { + c.WriteError(msgKeyNotFound) + return + } + + c.WriteLen(len(s.groups)) + for name, g := range s.groups { + c.WriteMapLen(4) + + c.WriteBulk("name") + c.WriteBulk(name) + + c.WriteBulk("consumers") + c.WriteInt(len(g.consumers)) + + c.WriteBulk("pending") + c.WriteInt(len(g.pending)) + + c.WriteBulk("last-delivered-id") + c.WriteBulk(g.lastID) + } + }) +} + +// XINFO CONSUMERS +// Please note that this is only a partial implementation, for it does not +// return each consumer's "idle" value, which indicates "the number of +// milliseconds that have passed since the consumer last interacted with the +// server." +func (m *Miniredis) cmdXinfoConsumers(c *server.Peer, args []string) { + if len(args) != 2 { + setDirty(c) + c.WriteError(errWrongNumber("CONSUMERS")) + return + } + key := args[0] + groupName := args[1] + + withTx(m, c, func(c *server.Peer, ctx *connCtx) { + db := m.db(ctx.selectedDB) + + s, err := db.stream(key) + if err != nil { + c.WriteError(err.Error()) + return + } + if s == nil { + c.WriteError(msgKeyNotFound) + return + } + + g, ok := s.groups[groupName] + if !ok { + err := fmt.Sprintf("NOGROUP No such consumer group '%s' for key name '%s'", groupName, key) + c.WriteError(err) + return + } + + consumerNames := make([]string, 0) + for name := range g.consumers { + consumerNames = append(consumerNames, name) + } + sort.Strings(consumerNames) + + c.WriteLen(len(consumerNames)) + for _, name := range consumerNames { + c.WriteMapLen(2) + + c.WriteBulk("name") + c.WriteBulk(name) + + c.WriteBulk("pending") + c.WriteInt(g.consumers[name].numPendingEntries) + } + }) +} + // XREADGROUP func (m *Miniredis) cmdXreadgroup(c *server.Peer, cmd string, args []string) { // XREADGROUP GROUP group consumer STREAMS key ID @@ -638,14 +884,16 @@ func (m *Miniredis) cmdXread(c *server.Peer, cmd string, args []string) { return } - var opts struct { - count int - streams []string - ids []string - block bool - blockTimeout time.Duration - } - var err error + var ( + opts struct { + count int + streams []string + ids []string + block bool + blockTimeout time.Duration + } + err error + ) parsing: for len(args) > 0 { @@ -676,11 +924,14 @@ parsing: } opts.streams, opts.ids = args[0:len(args)/2], args[len(args)/2:] - for _, id := range opts.ids { + for i, id := range opts.ids { if _, err := parseStreamID(id); id != `$` && err != nil { setDirty(c) c.WriteError(msgInvalidStreamID) return + } else if id == "$" { + db := m.DB(getCtx(c).selectedDB) + opts.ids[i] = db.streamKeys[opts.streams[i]].lastID() } } args = nil @@ -797,45 +1048,61 @@ func (m *Miniredis) cmdXpending(c *server.Peer, cmd string, args []string) { return } - key, group, args := args[0], args[1], args[2:] - summary := true - if len(args) > 0 && strings.ToUpper(args[0]) == "IDLE" { - setDirty(c) - c.WriteError("ERR IDLE is unsupported") - return - } - var ( + var opts struct { + key string + group string + summary bool + idle time.Duration start, end string count int consumer *string - ) + } + + opts.key, opts.group, args = args[0], args[1], args[2:] + opts.summary = true if len(args) >= 3 { - summary = false + opts.summary = false + + if strings.ToUpper(args[0]) == "IDLE" { + idleMs, err := strconv.ParseInt(args[1], 10, 64) + if err != nil { + setDirty(c) + c.WriteError(msgInvalidInt) + return + } + opts.idle = time.Duration(idleMs) * time.Millisecond - start_, err := formatStreamRangeBound(args[0], true, false) + args = args[2:] + if len(args) < 3 { + setDirty(c) + c.WriteError(msgSyntaxError) + return + } + } + + var err error + opts.start, err = formatStreamRangeBound(args[0], true, false) if err != nil { + setDirty(c) c.WriteError(msgInvalidStreamID) return } - start = start_ - end_, err := formatStreamRangeBound(args[1], false, false) + opts.end, err = formatStreamRangeBound(args[1], false, false) if err != nil { + setDirty(c) c.WriteError(msgInvalidStreamID) return } - end = end_ - n, err := strconv.Atoi(args[2]) // negative is allowed + opts.count, err = strconv.Atoi(args[2]) // negative is allowed if err != nil { + setDirty(c) c.WriteError(msgInvalidInt) return } - count = n args = args[3:] if len(args) == 1 { - var c string - c, args = args[0], args[1:] - consumer = &c + opts.consumer, args = &args[0], args[1:] } } if len(args) != 0 { @@ -846,21 +1113,21 @@ func (m *Miniredis) cmdXpending(c *server.Peer, cmd string, args []string) { withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) - g, err := db.streamGroup(key, group) + g, err := db.streamGroup(opts.key, opts.group) if err != nil { c.WriteError(err.Error()) return } if g == nil { - c.WriteError(errReadgroup(key, group).Error()) + c.WriteError(errReadgroup(opts.key, opts.group).Error()) return } - if summary { + if opts.summary { writeXpendingSummary(c, *g) return } - writeXpending(m.effectiveNow(), c, *g, start, end, count, consumer) + writeXpending(m.effectiveNow(), c, *g, opts.idle, opts.start, opts.end, opts.count, opts.consumer) }) } @@ -907,6 +1174,7 @@ func writeXpending( now time.Time, c *server.Peer, g streamGroup, + idle time.Duration, start, end string, count int, @@ -942,12 +1210,19 @@ func writeXpending( if streamCmp(p.id, end) > 0 { continue } - res = append(res, entry{ - id: p.id, - consumer: p.consumer, - millis: int(now.Sub(p.lastDelivery).Milliseconds()), - count: p.deliveryCount, - }) + timeSinceLastDelivery := now.Sub(p.lastDelivery) + if timeSinceLastDelivery >= idle { + res = append(res, entry{ + id: p.id, + consumer: p.consumer, + millis: int(timeSinceLastDelivery.Milliseconds()), + count: p.deliveryCount, + }) + } + } + if len(res) == 0 { + c.WriteLen(-1) + return } c.WriteLen(len(res)) for _, e := range res { @@ -1075,27 +1350,35 @@ func (m *Miniredis) cmdXautoclaim(c *server.Peer, cmd string, args []string) { return } - key, group, consumer := args[0], args[1], args[2] + var opts struct { + key string + group string + consumer string + minIdleTime time.Duration + start string + justId bool + count int + } + opts.key, opts.group, opts.consumer = args[0], args[1], args[2] n, err := strconv.Atoi(args[3]) if err != nil { setDirty(c) c.WriteError("ERR Invalid min-idle-time argument for XAUTOCLAIM") return } - minIdleTime := time.Millisecond * time.Duration(n) + opts.minIdleTime = time.Millisecond * time.Duration(n) start_, err := formatStreamRangeBound(args[4], true, false) if err != nil { c.WriteError(msgInvalidStreamID) return } - start := start_ + opts.start = start_ args = args[5:] - count := 100 - var justId bool + opts.count = 100 parsing: for len(args) > 0 { switch strings.ToUpper(args[0]) { @@ -1105,7 +1388,7 @@ parsing: break parsing } - count, err = strconv.Atoi(args[1]) + opts.count, err = strconv.Atoi(args[1]) if err != nil { break parsing } @@ -1113,7 +1396,7 @@ parsing: args = args[2:] case "JUSTID": args = args[1:] - justId = true + opts.justId = true default: err = errors.New(msgSyntaxError) break parsing @@ -1128,18 +1411,18 @@ parsing: withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) - g, err := db.streamGroup(key, group) + g, err := db.streamGroup(opts.key, opts.group) if err != nil { c.WriteError(err.Error()) return } if g == nil { - c.WriteError(errReadgroup(key, group).Error()) + c.WriteError(errReadgroup(opts.key, opts.group).Error()) return } - nextCallId, entries := xautoclaim(m.effectiveNow(), *g, minIdleTime, start, count, consumer) - writeXautoclaim(c, nextCallId, entries, justId) + nextCallId, entries := xautoclaim(m.effectiveNow(), *g, opts.minIdleTime, opts.start, opts.count, opts.consumer) + writeXautoclaim(c, nextCallId, entries, opts.justId) }) } @@ -1162,8 +1445,13 @@ func xautoclaim( if minIdleTime > 0 && now.Before(p.lastDelivery.Add(minIdleTime)) { continue } - g.consumers[consumerID] = consumer{} + + prevConsumerID := p.consumer + if _, ok := g.consumers[consumerID]; !ok { + g.consumers[consumerID] = &consumer{} + } p.consumer = consumerID + _, entry := g.stream.get(p.id) // not found. Weird? if entry == nil { @@ -1172,8 +1460,13 @@ func xautoclaim( // (Introduced in Redis 7.0) continue } + p.deliveryCount += 1 p.lastDelivery = now + + g.consumers[prevConsumerID].numPendingEntries-- + g.consumers[consumerID].numPendingEntries++ + msgs[i] = p res = append(res, *entry) @@ -1206,6 +1499,192 @@ func writeXautoclaim(c *server.Peer, nextCallId string, res []StreamEntry, justI } } +// XCLAIM +func (m *Miniredis) cmdXclaim(c *server.Peer, cmd string, args []string) { + if len(args) < 5 { + setDirty(c) + c.WriteError(errWrongNumber(cmd)) + return + } + + var opts struct { + key string + groupName string + consumerName string + minIdleTime time.Duration + newLastDelivery time.Time + ids []string + retryCount *int + force bool + justId bool + } + + opts.key, opts.groupName, opts.consumerName = args[0], args[1], args[2] + + minIdleTimeMillis, err := strconv.Atoi(args[3]) + if err != nil { + setDirty(c) + c.WriteError("ERR Invalid min-idle-time argument for XCLAIM") + return + } + opts.minIdleTime = time.Millisecond * time.Duration(minIdleTimeMillis) + + opts.newLastDelivery = m.effectiveNow() + opts.ids = append(opts.ids, args[4]) + + args = args[5:] + for len(args) > 0 { + arg := strings.ToUpper(args[0]) + if arg == "IDLE" || + arg == "TIME" || + arg == "RETRYCOUNT" || + arg == "FORCE" || + arg == "JUSTID" { + break + } + opts.ids = append(opts.ids, arg) + args = args[1:] + } + + for len(args) > 0 { + arg := strings.ToUpper(args[0]) + switch arg { + case "IDLE": + idleMs, err := strconv.ParseInt(args[1], 10, 64) + if err != nil { + setDirty(c) + c.WriteError("ERR Invalid IDLE option argument for XCLAIM") + return + } + if idleMs < 0 { + idleMs = 0 + } + opts.newLastDelivery = m.effectiveNow().Add(time.Millisecond * time.Duration(-idleMs)) + args = args[2:] + case "TIME": + timeMs, err := strconv.ParseInt(args[1], 10, 64) + if err != nil { + setDirty(c) + c.WriteError("ERR Invalid TIME option argument for XCLAIM") + return + } + opts.newLastDelivery = unixMilli(timeMs) + args = args[2:] + case "RETRYCOUNT": + retryCount, err := strconv.Atoi(args[1]) + if err != nil { + setDirty(c) + c.WriteError("ERR Invalid RETRYCOUNT option argument for XCLAIM") + return + } + opts.retryCount = &retryCount + args = args[2:] + case "FORCE": + opts.force = true + args = args[1:] + case "JUSTID": + opts.justId = true + args = args[1:] + default: + setDirty(c) + c.WriteError(fmt.Sprintf("ERR Unrecognized XCLAIM option '%s'", args[0])) + return + } + } + + withTx(m, c, func(c *server.Peer, ctx *connCtx) { + db := m.db(ctx.selectedDB) + + g, err := db.streamGroup(opts.key, opts.groupName) + if err != nil { + c.WriteError(err.Error()) + return + } + if g == nil { + c.WriteError(errReadgroup(opts.key, opts.groupName).Error()) + return + } + + claimedEntryIDs := m.xclaim(g, opts.consumerName, opts.minIdleTime, opts.newLastDelivery, opts.ids, opts.retryCount, opts.force) + writeXclaim(c, g.stream, claimedEntryIDs, opts.justId) + }) +} + +func (m *Miniredis) xclaim( + group *streamGroup, + consumerName string, + minIdleTime time.Duration, + newLastDelivery time.Time, + ids []string, + retryCount *int, + force bool, +) (claimedEntryIDs []string) { + for _, id := range ids { + pelPos, pelEntry := group.searchPending(id) + if pelEntry == nil { + if !force { + continue + } + + if pelPos < len(group.pending) { + group.pending = append(group.pending[:pelPos+1], group.pending[pelPos:]...) + } else { + group.pending = append(group.pending, pendingEntry{}) + } + pelEntry = &group.pending[pelPos] + + *pelEntry = pendingEntry{ + id: id, + consumer: consumerName, + deliveryCount: 1, + } + } else { + group.consumers[pelEntry.consumer].numPendingEntries-- + pelEntry.consumer = consumerName + } + + if retryCount != nil { + pelEntry.deliveryCount = *retryCount + } else { + pelEntry.deliveryCount++ + } + pelEntry.lastDelivery = newLastDelivery + + claimedEntryIDs = append(claimedEntryIDs, id) + } + if len(claimedEntryIDs) == 0 { + return + } + + if _, ok := group.consumers[consumerName]; !ok { + group.consumers[consumerName] = &consumer{} + } + consumer := group.consumers[consumerName] + consumer.numPendingEntries += len(claimedEntryIDs) + + return +} + +func writeXclaim(c *server.Peer, stream *streamKey, claimedEntryIDs []string, justId bool) { + c.WriteLen(len(claimedEntryIDs)) + for _, id := range claimedEntryIDs { + if justId { + c.WriteBulk(id) + continue + } + + _, entry := stream.get(id) + if entry == nil { + c.WriteNull() + continue + } + + c.WriteLen(2) + c.WriteBulk(entry.ID) + c.WriteStrings(entry.Values) + } +} + func parseBlock(cmd string, args []string, block *bool, timeout *time.Duration) error { if len(args) < 2 { return errors.New(errWrongNumber(cmd)) @@ -1221,3 +1700,8 @@ func parseBlock(cmd string, args []string, block *bool, timeout *time.Duration) (*timeout) = time.Millisecond * time.Duration(ms) return nil } + +// taken from Go's time package. Can be dropped if miniredis supports >= 1.17 +func unixMilli(msec int64) time.Time { + return time.Unix(msec/1e3, (msec%1e3)*1e6) +} diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_string.go b/vendor/github.com/alicebob/miniredis/v2/cmd_string.go index 29c2c22734..cec9d48343 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_string.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_string.go @@ -227,26 +227,29 @@ func (m *Miniredis) cmdPsetex(c *server.Peer, cmd string, args []string) { return } - key := args[0] - ttl, err := strconv.Atoi(args[1]) - if err != nil { - setDirty(c) - c.WriteError(msgInvalidInt) + var opts struct { + key string + ttl int + value string + } + + opts.key = args[0] + if ok := optInt(c, args[1], &opts.ttl); !ok { return } - if ttl <= 0 { + if opts.ttl <= 0 { setDirty(c) c.WriteError(msgInvalidPSETEXTime) return } - value := args[2] + opts.value = args[2] withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) - db.del(key, true) // Clear any existing keys. - db.stringSet(key, value) - db.ttl[key] = time.Duration(ttl) * time.Millisecond + db.del(opts.key, true) // Clear any existing keys. + db.stringSet(opts.key, opts.value) + db.ttl[opts.key] = time.Duration(opts.ttl) * time.Millisecond c.WriteOK() }) } @@ -634,23 +637,24 @@ func (m *Miniredis) cmdIncrby(c *server.Peer, cmd string, args []string) { return } - key := args[0] - delta, err := strconv.Atoi(args[1]) - if err != nil { - setDirty(c) - c.WriteError(msgInvalidInt) + var opts struct { + key string + delta int + } + opts.key = args[0] + if ok := optInt(c, args[1], &opts.delta); !ok { return } withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) - if t, ok := db.keys[key]; ok && t != "string" { + if t, ok := db.keys[opts.key]; ok && t != "string" { c.WriteError(msgWrongType) return } - v, err := db.stringIncr(key, delta) + v, err := db.stringIncr(opts.key, opts.delta) if err != nil { c.WriteError(err.Error()) return @@ -746,23 +750,24 @@ func (m *Miniredis) cmdDecrby(c *server.Peer, cmd string, args []string) { return } - key := args[0] - delta, err := strconv.Atoi(args[1]) - if err != nil { - setDirty(c) - c.WriteError(msgInvalidInt) + var opts struct { + key string + delta int + } + opts.key = args[0] + if ok := optInt(c, args[1], &opts.delta); !ok { return } withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) - if t, ok := db.keys[key]; ok && t != "string" { + if t, ok := db.keys[opts.key]; ok && t != "string" { c.WriteError(msgWrongType) return } - v, err := db.stringIncr(key, -delta) + v, err := db.stringIncr(opts.key, -opts.delta) if err != nil { c.WriteError(err.Error()) return @@ -845,30 +850,29 @@ func (m *Miniredis) cmdGetrange(c *server.Peer, cmd string, args []string) { return } - key := args[0] - start, err := strconv.Atoi(args[1]) - if err != nil { - setDirty(c) - c.WriteError(msgInvalidInt) + var opts struct { + key string + start int + end int + } + opts.key = args[0] + if ok := optInt(c, args[1], &opts.start); !ok { return } - end, err := strconv.Atoi(args[2]) - if err != nil { - setDirty(c) - c.WriteError(msgInvalidInt) + if ok := optInt(c, args[2], &opts.end); !ok { return } withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) - if t, ok := db.keys[key]; ok && t != "string" { + if t, ok := db.keys[opts.key]; ok && t != "string" { c.WriteError(msgWrongType) return } - v := db.stringKeys[key] - c.WriteBulk(withRange(v, start, end)) + v := db.stringKeys[opts.key] + c.WriteBulk(withRange(v, opts.start, opts.end)) }) } @@ -886,36 +890,39 @@ func (m *Miniredis) cmdSetrange(c *server.Peer, cmd string, args []string) { return } - key := args[0] - pos, err := strconv.Atoi(args[1]) - if err != nil { - setDirty(c) - c.WriteError(msgInvalidInt) + var opts struct { + key string + pos int + subst string + } + opts.key = args[0] + if ok := optInt(c, args[1], &opts.pos); !ok { return } - if pos < 0 { + if opts.pos < 0 { setDirty(c) c.WriteError("ERR offset is out of range") return } - subst := args[2] + opts.subst = args[2] withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) - if t, ok := db.keys[key]; ok && t != "string" { + if t, ok := db.keys[opts.key]; ok && t != "string" { c.WriteError(msgWrongType) return } - v := []byte(db.stringKeys[key]) - if len(v) < pos+len(subst) { - newV := make([]byte, pos+len(subst)) + v := []byte(db.stringKeys[opts.key]) + end := opts.pos + len(opts.subst) + if len(v) < end { + newV := make([]byte, end) copy(newV, v) v = newV } - copy(v[pos:pos+len(subst)], subst) - db.stringSet(key, string(v)) + copy(v[opts.pos:end], opts.subst) + db.stringSet(opts.key, string(v)) c.WriteInt(len(v)) }) } @@ -935,28 +942,20 @@ func (m *Miniredis) cmdBitcount(c *server.Peer, cmd string, args []string) { } var opts struct { - useRange bool - start, end int - key string + useRange bool + start int + end int + key string } opts.key, args = args[0], args[1:] if len(args) >= 2 { opts.useRange = true - var err error - n, err := strconv.Atoi(args[0]) - if err != nil { - setDirty(c) - c.WriteError(msgInvalidInt) + if ok := optInt(c, args[0], &opts.start); !ok { return } - opts.start = n - n, err = strconv.Atoi(args[1]) - if err != nil { - setDirty(c) - c.WriteError(msgInvalidInt) + if ok := optInt(c, args[1], &opts.end); !ok { return } - opts.end = n args = args[2:] } @@ -1001,25 +1000,28 @@ func (m *Miniredis) cmdBitop(c *server.Peer, cmd string, args []string) { return } - var ( - op = strings.ToUpper(args[0]) - target = args[1] - input = args[2:] - ) + var opts struct { + op string + target string + input []string + } + opts.op = strings.ToUpper(args[0]) + opts.target = args[1] + opts.input = args[2:] // 'op' is tested when the transaction is executed. withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) - switch op { + switch opts.op { case "AND", "OR", "XOR": - first := input[0] + first := opts.input[0] if t, ok := db.keys[first]; ok && t != "string" { c.WriteError(msgWrongType) return } res := []byte(db.stringKeys[first]) - for _, vk := range input[1:] { + for _, vk := range opts.input[1:] { if t, ok := db.keys[vk]; ok && t != "string" { c.WriteError(msgWrongType) return @@ -1029,23 +1031,23 @@ func (m *Miniredis) cmdBitop(c *server.Peer, cmd string, args []string) { "AND": func(a, b byte) byte { return a & b }, "OR": func(a, b byte) byte { return a | b }, "XOR": func(a, b byte) byte { return a ^ b }, - }[op] + }[opts.op] res = sliceBinOp(cb, res, []byte(v)) } - db.del(target, false) // Keep TTL + db.del(opts.target, false) // Keep TTL if len(res) == 0 { - db.del(target, true) + db.del(opts.target, true) } else { - db.stringSet(target, string(res)) + db.stringSet(opts.target, string(res)) } c.WriteInt(len(res)) case "NOT": // NOT only takes a single argument. - if len(input) != 1 { + if len(opts.input) != 1 { c.WriteError("ERR BITOP NOT must be called with a single source key.") return } - key := input[0] + key := opts.input[0] if t, ok := db.keys[key]; ok && t != "string" { c.WriteError(msgWrongType) return @@ -1054,11 +1056,11 @@ func (m *Miniredis) cmdBitop(c *server.Peer, cmd string, args []string) { for i := range value { value[i] = ^value[i] } - db.del(target, false) // Keep TTL + db.del(opts.target, false) // Keep TTL if len(value) == 0 { - db.del(target, true) + db.del(opts.target, true) } else { - db.stringSet(target, string(value)) + db.stringSet(opts.target, string(value)) } c.WriteInt(len(value)) default: @@ -1182,9 +1184,15 @@ func (m *Miniredis) cmdGetbit(c *server.Peer, cmd string, args []string) { return } - key := args[0] - bit, err := strconv.Atoi(args[1]) - if err != nil { + var opts struct { + key string + bit int + } + opts.key = args[0] + if ok := optIntErr(c, args[1], &opts.bit, "ERR bit offset is not an integer or out of range"); !ok { + return + } + if opts.bit < 0 { setDirty(c) c.WriteError("ERR bit offset is not an integer or out of range") return @@ -1193,13 +1201,13 @@ func (m *Miniredis) cmdGetbit(c *server.Peer, cmd string, args []string) { withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) - if t, ok := db.keys[key]; ok && t != "string" { + if t, ok := db.keys[opts.key]; ok && t != "string" { c.WriteError(msgWrongType) return } - value := db.stringKeys[key] + value := db.stringKeys[opts.key] - ourByteNr := bit / 8 + ourByteNr := opts.bit / 8 var ourByte byte if ourByteNr > len(value)-1 { ourByte = '\x00' @@ -1207,7 +1215,7 @@ func (m *Miniredis) cmdGetbit(c *server.Peer, cmd string, args []string) { ourByte = value[ourByteNr] } res := 0 - if toBits(ourByte)[bit%8] { + if toBits(ourByte)[opts.bit%8] { res = 1 } c.WriteInt(res) @@ -1228,15 +1236,24 @@ func (m *Miniredis) cmdSetbit(c *server.Peer, cmd string, args []string) { return } - key := args[0] - bit, err := strconv.Atoi(args[1]) - if err != nil || bit < 0 { + var opts struct { + key string + bit int + newBit int + } + opts.key = args[0] + if ok := optIntErr(c, args[1], &opts.bit, "ERR bit offset is not an integer or out of range"); !ok { + return + } + if opts.bit < 0 { setDirty(c) c.WriteError("ERR bit offset is not an integer or out of range") return } - newBit, err := strconv.Atoi(args[2]) - if err != nil || (newBit != 0 && newBit != 1) { + if ok := optIntErr(c, args[2], &opts.newBit, "ERR bit is not an integer or out of range"); !ok { + return + } + if opts.newBit != 0 && opts.newBit != 1 { setDirty(c) c.WriteError("ERR bit is not an integer or out of range") return @@ -1245,14 +1262,14 @@ func (m *Miniredis) cmdSetbit(c *server.Peer, cmd string, args []string) { withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) - if t, ok := db.keys[key]; ok && t != "string" { + if t, ok := db.keys[opts.key]; ok && t != "string" { c.WriteError(msgWrongType) return } - value := []byte(db.stringKeys[key]) + value := []byte(db.stringKeys[opts.key]) - ourByteNr := bit / 8 - ourBitNr := bit % 8 + ourByteNr := opts.bit / 8 + ourBitNr := opts.bit % 8 if ourByteNr > len(value)-1 { // Too short. Expand. newValue := make([]byte, ourByteNr+1) @@ -1263,12 +1280,12 @@ func (m *Miniredis) cmdSetbit(c *server.Peer, cmd string, args []string) { if toBits(value[ourByteNr])[ourBitNr] { old = 1 } - if newBit == 0 { + if opts.newBit == 0 { value[ourByteNr] &^= 1 << uint8(7-ourBitNr) } else { value[ourByteNr] |= 1 << uint8(7-ourBitNr) } - db.stringSet(key, string(value)) + db.stringSet(opts.key, string(value)) c.WriteInt(old) }) diff --git a/vendor/github.com/alicebob/miniredis/v2/geo.go b/vendor/github.com/alicebob/miniredis/v2/geo.go index bc8e929270..3028a16701 100644 --- a/vendor/github.com/alicebob/miniredis/v2/geo.go +++ b/vendor/github.com/alicebob/miniredis/v2/geo.go @@ -21,12 +21,10 @@ func hsin(theta float64) float64 { } // distance function returns the distance (in meters) between two points of -// a given longitude and latitude relatively accurately (using a spherical -// approximation of the Earth) through the Haversin Distance Formula for -// great arc distance on a sphere with accuracy for small distances -// +// a given longitude and latitude relatively accurately (using a spherical +// approximation of the Earth) through the Haversin Distance Formula for +// great arc distance on a sphere with accuracy for small distances // point coordinates are supplied in degrees and converted into rad. in the func -// // distance returned is meters // http://en.wikipedia.org/wiki/Haversine_formula // Source: https://gist.github.com/cdipaolo/d3f8db3848278b49db68 diff --git a/vendor/github.com/alicebob/miniredis/v2/lua.go b/vendor/github.com/alicebob/miniredis/v2/lua.go index 42222dce8e..bc86eed7d4 100644 --- a/vendor/github.com/alicebob/miniredis/v2/lua.go +++ b/vendor/github.com/alicebob/miniredis/v2/lua.go @@ -217,6 +217,8 @@ func redisToLua(l *lua.LState, res []interface{}) *lua.LTable { v = lua.LFalse } else { switch et := e.(type) { + case int: + v = lua.LNumber(et) case int64: v = lua.LNumber(et) case []uint8: diff --git a/vendor/github.com/alicebob/miniredis/v2/miniredis.go b/vendor/github.com/alicebob/miniredis/v2/miniredis.go index 697dec0652..c86e2fe8b3 100644 --- a/vendor/github.com/alicebob/miniredis/v2/miniredis.go +++ b/vendor/github.com/alicebob/miniredis/v2/miniredis.go @@ -13,7 +13,6 @@ // // For direct use you can select a Redis database with either `s.Select(12); // s.Get("foo")` or `s.DB(12).Get("foo")`. -// package miniredis import ( @@ -194,7 +193,6 @@ func (m *Miniredis) start(s *server.Server) error { commandsScripting(m) commandsGeo(m) commandsCluster(m) - commandsCommand(m) commandsHll(m) return nil @@ -347,9 +345,9 @@ func (m *Miniredis) Server() *server.Server { // Dump limits the maximum length of each key:value to "DumpMaxLineLen" characters. // To increase that, call something like: // -// miniredis.DumpMaxLineLen = 1024 -// mr, _ = miniredis.Run() -// mr.Dump() +// miniredis.DumpMaxLineLen = 1024 +// mr, _ = miniredis.Run() +// mr.Dump() func (m *Miniredis) Dump() string { m.Lock() defer m.Unlock() @@ -419,8 +417,10 @@ func (m *Miniredis) SetTime(t time.Time) { } // make every command return this message. For example: -// LOADING Redis is loading the dataset in memory -// MASTERDOWN Link with MASTER is down and replica-serve-stale-data is set to 'no'. +// +// LOADING Redis is loading the dataset in memory +// MASTERDOWN Link with MASTER is down and replica-serve-stale-data is set to 'no'. +// // Clear it with an empty string. Don't add newlines. func (m *Miniredis) SetError(msg string) { cb := server.Hook(nil) @@ -433,6 +433,18 @@ func (m *Miniredis) SetError(msg string) { m.srv.SetPreHook(cb) } +// isValidCMD returns true if command is valid and can be executed. +func (m *Miniredis) isValidCMD(c *server.Peer, cmd string) bool { + if !m.handleAuth(c) { + return false + } + if m.checkPubsub(c, cmd) { + return false + } + + return true +} + // handleAuth returns false if connection has no access. It sends the reply. func (m *Miniredis) handleAuth(c *server.Peer) bool { if getCtx(c).nested { diff --git a/vendor/github.com/alicebob/miniredis/v2/opts.go b/vendor/github.com/alicebob/miniredis/v2/opts.go index 016d268200..de91386015 100644 --- a/vendor/github.com/alicebob/miniredis/v2/opts.go +++ b/vendor/github.com/alicebob/miniredis/v2/opts.go @@ -1,7 +1,9 @@ package miniredis import ( + "math" "strconv" + "time" "github.com/alicebob/miniredis/v2/server" ) @@ -10,12 +12,33 @@ import ( // Writes "invalid integer" error to c if it's not a valid integer. Returns // whether or not things were okay. func optInt(c *server.Peer, src string, dest *int) bool { + return optIntErr(c, src, dest, msgInvalidInt) +} + +func optIntErr(c *server.Peer, src string, dest *int, errMsg string) bool { n, err := strconv.Atoi(src) if err != nil { setDirty(c) - c.WriteError(msgInvalidInt) + c.WriteError(errMsg) return false } *dest = n return true } + +func optDuration(c *server.Peer, src string, dest *time.Duration) bool { + n, err := strconv.ParseFloat(src, 64) + if err != nil { + setDirty(c) + c.WriteError(msgInvalidTimeout) + return false + } + if n < 0 || math.IsInf(n, 0) { + setDirty(c) + c.WriteError(msgNegTimeout) + return false + } + + *dest = time.Duration(n*1_000_000) * time.Microsecond + return true +} diff --git a/vendor/github.com/alicebob/miniredis/v2/redis.go b/vendor/github.com/alicebob/miniredis/v2/redis.go index d4a0cd8d44..7e78ee4706 100644 --- a/vendor/github.com/alicebob/miniredis/v2/redis.go +++ b/vendor/github.com/alicebob/miniredis/v2/redis.go @@ -49,6 +49,9 @@ const ( msgXtrimInvalidLimit = "ERR syntax error, LIMIT cannot be used without the special ~ option" msgDBIndexOutOfRange = "ERR DB index is out of range" msgLimitCombination = "ERR syntax error, LIMIT is only supported in combination with either BYSCORE or BYLEX" + msgRankIsZero = "ERR RANK can't be zero: use 1 to start from the first match, 2 from the second ... or use negative to start from the end of the list" + msgCountIsNegative = "ERR COUNT can't be negative" + msgMaxLengthIsNegative = "ERR MAXLEN can't be negative" ) func errWrongNumber(cmd string) string { @@ -134,14 +137,19 @@ func blocking( m.Lock() defer m.Unlock() for { - done := cb(c, ctx) - if done { + if c.Closed() { return } if m.Ctx.Err() != nil { return } + + done := cb(c, ctx) + if done { + return + } + if timedOut { onTimeout(c) return diff --git a/vendor/github.com/alicebob/miniredis/v2/server/server.go b/vendor/github.com/alicebob/miniredis/v2/server/server.go index 60e391f22b..ee4f04c218 100644 --- a/vendor/github.com/alicebob/miniredis/v2/server/server.go +++ b/vendor/github.com/alicebob/miniredis/v2/server/server.go @@ -158,24 +158,34 @@ func (s *Server) servePeer(c net.Conn) { peer := &Peer{ w: bufio.NewWriter(c), } + defer func() { for _, f := range peer.onDisconnect { f() } }() - for { - args, err := readArray(r) - if err != nil { - return + readCh := make(chan []string) + + go func() { + defer close(readCh) + + for { + args, err := readArray(r) + if err != nil { + peer.Close() + return + } + + readCh <- args } + }() + + for args := range readCh { s.Dispatch(peer, args) peer.Flush() - s.mu.Lock() - closed := peer.closed - s.mu.Unlock() - if closed { + if peer.Closed() { c.Close() } } @@ -259,6 +269,13 @@ func (c *Peer) Close() { c.closed = true } +// Return true if the peer connection closed. +func (c *Peer) Closed() bool { + c.mu.Lock() + defer c.mu.Unlock() + return c.closed +} + // Register a function to execute on disconnect. There can be multiple // functions registered. func (c *Peer) OnDisconnect(f func()) { diff --git a/vendor/github.com/alicebob/miniredis/v2/stream.go b/vendor/github.com/alicebob/miniredis/v2/stream.go index 574f9016dc..75848d0280 100644 --- a/vendor/github.com/alicebob/miniredis/v2/stream.go +++ b/vendor/github.com/alicebob/miniredis/v2/stream.go @@ -9,6 +9,7 @@ import ( "sort" "strconv" "strings" + "sync" "time" ) @@ -17,6 +18,7 @@ type streamKey struct { entries []StreamEntry groups map[string]*streamGroup lastAllocatedID string + mu sync.Mutex } // a StreamEntry is an entry in a stream. The ID is always of the form @@ -31,10 +33,11 @@ type streamGroup struct { stream *streamKey lastID string pending []pendingEntry - consumers map[string]consumer + consumers map[string]*consumer } type consumer struct { + numPendingEntries int // TODO: "last seen" timestamp } @@ -51,6 +54,7 @@ func newStreamKey() *streamKey { } } +// generateID doesn't lock the mutex func (s *streamKey) generateID(now time.Time) string { ts := uint64(now.UnixNano()) / 1_000_000 @@ -60,7 +64,7 @@ func (s *streamKey) generateID(now time.Time) string { next = fmt.Sprintf("%d-%d", last[0], last[1]+1) } - lastID := s.lastID() + lastID := s.lastIDUnlocked() if streamCmp(lastID, next) >= 0 { last, _ := parseStreamID(lastID) next = fmt.Sprintf("%d-%d", last[0], last[1]+1) @@ -70,7 +74,16 @@ func (s *streamKey) generateID(now time.Time) string { return next } +// lastID locks the mutex func (s *streamKey) lastID() string { + s.mu.Lock() + defer s.mu.Unlock() + + return s.lastIDUnlocked() +} + +// lastID doesn't lock the mutex +func (s *streamKey) lastIDUnlocked() string { if len(s.entries) == 0 { return "0-0" } @@ -79,6 +92,9 @@ func (s *streamKey) lastID() string { } func (s *streamKey) copy() *streamKey { + s.mu.Lock() + defer s.mu.Unlock() + cpy := &streamKey{ entries: s.entries, } @@ -193,17 +209,20 @@ func reversedStreamEntries(o []StreamEntry) []StreamEntry { } func (s *streamKey) createGroup(group, id string) error { + s.mu.Lock() + defer s.mu.Unlock() + if _, ok := s.groups[group]; ok { return errors.New("BUSYGROUP Consumer Group name already exists") } if id == "$" { - id = s.lastID() + id = s.lastIDUnlocked() } s.groups[group] = &streamGroup{ stream: s, lastID: id, - consumers: map[string]consumer{}, + consumers: map[string]*consumer{}, } return nil } @@ -212,6 +231,9 @@ func (s *streamKey) createGroup(group, id string) error { // If id is empty or "*" the ID will be generated automatically. // `values` should have an even length. func (s *streamKey) add(entryID string, values []string, now time.Time) (string, error) { + s.mu.Lock() + defer s.mu.Unlock() + if entryID == "" || entryID == "*" { entryID = s.generateID(now) } @@ -223,7 +245,7 @@ func (s *streamKey) add(entryID string, values []string, now time.Time) (string, if entryID == "0-0" { return "", errors.New(msgStreamIDZero) } - if streamCmp(s.lastID(), entryID) != -1 { + if streamCmp(s.lastIDUnlocked(), entryID) != -1 { return "", errors.New(msgStreamIDTooSmall) } @@ -235,6 +257,9 @@ func (s *streamKey) add(entryID string, values []string, now time.Time) (string, } func (s *streamKey) trim(n int) { + s.mu.Lock() + defer s.mu.Unlock() + if len(s.entries) > n { s.entries = s.entries[len(s.entries)-n:] } @@ -242,6 +267,9 @@ func (s *streamKey) trim(n int) { // all entries after "id" func (s *streamKey) after(id string) []StreamEntry { + s.mu.Lock() + defer s.mu.Unlock() + pos := sort.Search(len(s.entries), func(i int) bool { return streamCmp(id, s.entries[i].ID) < 0 }) @@ -251,6 +279,9 @@ func (s *streamKey) after(id string) []StreamEntry { // get a stream entry by ID // Also returns the position in the entries slice, if found. func (s *streamKey) get(id string) (int, *StreamEntry) { + s.mu.Lock() + defer s.mu.Unlock() + pos := sort.Search(len(s.entries), func(i int) bool { return streamCmp(id, s.entries[i].ID) <= 0 }) @@ -279,16 +310,39 @@ func (g *streamGroup) readGroup( } if !noack { + shouldAppend := len(g.pending) == 0 for _, msg := range msgs { - g.pending = append(g.pending, pendingEntry{ + if !shouldAppend { + shouldAppend = streamCmp(msg.ID, g.pending[len(g.pending)-1].id) == 1 + } + + var entry *pendingEntry + if shouldAppend { + g.pending = append(g.pending, pendingEntry{}) + entry = &g.pending[len(g.pending)-1] + } else { + var pos int + pos, entry = g.searchPending(msg.ID) + if entry == nil { + g.pending = append(g.pending[:pos+1], g.pending[pos:]...) + entry = &g.pending[pos] + } else { + g.consumers[entry.consumer].numPendingEntries-- + } + } + + *entry = pendingEntry{ id: msg.ID, consumer: consumerID, deliveryCount: 1, lastDelivery: now, - }) + } } } - g.consumers[consumerID] = consumer{} + if _, ok := g.consumers[consumerID]; !ok { + g.consumers[consumerID] = &consumer{} + } + g.consumers[consumerID].numPendingEntries += len(msgs) g.lastID = msgs[len(msgs)-1].ID return msgs } @@ -314,6 +368,16 @@ func (g *streamGroup) readGroup( return res } +func (g *streamGroup) searchPending(id string) (int, *pendingEntry) { + pos := sort.Search(len(g.pending), func(i int) bool { + return streamCmp(id, g.pending[i].id) <= 0 + }) + if pos >= len(g.pending) || g.pending[pos].id != id { + return pos, nil + } + return pos, &g.pending[pos] +} + func (g *streamGroup) ack(ids []string) (int, error) { count := 0 for _, id := range ids { @@ -321,13 +385,14 @@ func (g *streamGroup) ack(ids []string) (int, error) { return 0, errors.New(msgInvalidStreamID) } - pos := sort.Search(len(g.pending), func(i int) bool { - return streamCmp(id, g.pending[i].id) <= 0 - }) - if len(g.pending) <= pos || g.pending[pos].id != id { + pos, entry := g.searchPending(id) + if entry == nil { continue } + consumer := g.consumers[entry.consumer] + consumer.numPendingEntries-- + g.pending = append(g.pending[:pos], g.pending[pos+1:]...) count++ } @@ -370,9 +435,10 @@ func (g *streamGroup) pendingCount(consumer string) int { } func (g *streamGroup) copy() *streamGroup { - cns := map[string]consumer{} + cns := map[string]*consumer{} for k, v := range g.consumers { - cns[k] = v + c := *v + cns[k] = &c } return &streamGroup{ // don't copy stream diff --git a/vendor/github.com/yuin/gopher-lua/README.rst b/vendor/github.com/yuin/gopher-lua/README.rst index b479e46357..2b6de3259a 100644 --- a/vendor/github.com/yuin/gopher-lua/README.rst +++ b/vendor/github.com/yuin/gopher-lua/README.rst @@ -870,6 +870,7 @@ Libraries for GopherLua - `gluaperiphery `_ : A periphery library for the GopherLua VM (GPIO, SPI, I2C, MMIO, and Serial peripheral I/O for Linux). - `glua-async `_ : An async/await implement for gopher-lua. - `gopherlua-debugger `_ : A debugger for gopher-lua +- `gluamahonia `_ : An encoding converter for gopher-lua ---------------------------------------------------------------- Donation ---------------------------------------------------------------- diff --git a/vendor/github.com/yuin/gopher-lua/_vm.go b/vendor/github.com/yuin/gopher-lua/_vm.go index 874ed9aa4a..049107e177 100644 --- a/vendor/github.com/yuin/gopher-lua/_vm.go +++ b/vendor/github.com/yuin/gopher-lua/_vm.go @@ -415,7 +415,7 @@ func init() { if ret.Type() == LTNumber { reg.SetNumber(RA, ret.(LNumber)) } else { - reg.SetNumber(RA, LNumber(0)) + reg.Set(RA, ret) } } else if lv.Type() == LTTable { reg.SetNumber(RA, LNumber(lv.(*LTable).Len())) diff --git a/vendor/github.com/yuin/gopher-lua/ast/expr.go b/vendor/github.com/yuin/gopher-lua/ast/expr.go index ccda327910..388852bab3 100644 --- a/vendor/github.com/yuin/gopher-lua/ast/expr.go +++ b/vendor/github.com/yuin/gopher-lua/ast/expr.go @@ -52,6 +52,7 @@ type StringExpr struct { type Comma3Expr struct { ExprBase + AdjustRet bool } type IdentExpr struct { diff --git a/vendor/github.com/yuin/gopher-lua/baselib.go b/vendor/github.com/yuin/gopher-lua/baselib.go index 06c90619ee..aa2c08a941 100644 --- a/vendor/github.com/yuin/gopher-lua/baselib.go +++ b/vendor/github.com/yuin/gopher-lua/baselib.go @@ -260,7 +260,7 @@ func basePairs(L *LState) int { func basePCall(L *LState) int { L.CheckAny(1) v := L.Get(1) - if v.Type() != LTFunction { + if v.Type() != LTFunction && L.GetMetaField(v, "__call").Type() != LTFunction { L.Push(LFalse) L.Push(LString("attempt to call a " + v.Type().String() + " value")) return 2 @@ -321,11 +321,16 @@ func baseSelect(L *LState) int { switch lv := L.Get(1).(type) { case LNumber: idx := int(lv) - num := L.reg.Top() - L.indexToReg(int(lv)) - 1 + num := L.GetTop() if idx < 0 { - num++ + idx = num + idx + } else if idx > num { + idx = num } - return num + if 1 > idx { + L.ArgError(1, "index out of range") + } + return num - idx case LString: if string(lv) != "#" { L.ArgError(1, "invalid string '"+string(lv)+"'") diff --git a/vendor/github.com/yuin/gopher-lua/compile.go b/vendor/github.com/yuin/gopher-lua/compile.go index d3c665ae57..75c75550e4 100644 --- a/vendor/github.com/yuin/gopher-lua/compile.go +++ b/vendor/github.com/yuin/gopher-lua/compile.go @@ -114,7 +114,7 @@ func isVarArgReturnExpr(expr ast.Expr) bool { case *ast.FuncCallExpr: return !ex.AdjustRet case *ast.Comma3Expr: - return true + return !ex.AdjustRet } return false } @@ -723,8 +723,12 @@ func compileReturnStmt(context *funcContext, stmt *ast.ReturnStmt) { // {{{ return } case *ast.FuncCallExpr: - reg += compileExpr(context, reg, ex, ecnone(-2)) - code.SetOpCode(code.LastPC(), OP_TAILCALL) + if ex.AdjustRet { // return (func()) + reg += compileExpr(context, reg, ex, ecnone(0)) + } else { + reg += compileExpr(context, reg, ex, ecnone(-2)) + code.SetOpCode(code.LastPC(), OP_TAILCALL) + } code.AddABC(OP_RETURN, a, 0, 0, sline(stmt)) return } diff --git a/vendor/github.com/yuin/gopher-lua/config.go b/vendor/github.com/yuin/gopher-lua/config.go index f58b59393a..d632188953 100644 --- a/vendor/github.com/yuin/gopher-lua/config.go +++ b/vendor/github.com/yuin/gopher-lua/config.go @@ -22,15 +22,22 @@ var LuaPath = "LUA_PATH" var LuaLDir string var LuaPathDefault string var LuaOS string +var LuaDirSep string +var LuaPathSep = ";" +var LuaPathMark = "?" +var LuaExecDir = "!" +var LuaIgMark = "-" func init() { if os.PathSeparator == '/' { // unix-like LuaOS = "unix" LuaLDir = "/usr/local/share/lua/5.1" + LuaDirSep = "/" LuaPathDefault = "./?.lua;" + LuaLDir + "/?.lua;" + LuaLDir + "/?/init.lua" } else { // windows LuaOS = "windows" LuaLDir = "!\\lua" + LuaDirSep = "\\" LuaPathDefault = ".\\?.lua;" + LuaLDir + "\\?.lua;" + LuaLDir + "\\?\\init.lua" } } diff --git a/vendor/github.com/yuin/gopher-lua/loadlib.go b/vendor/github.com/yuin/gopher-lua/loadlib.go index 772bb04ad8..40ce122b8f 100644 --- a/vendor/github.com/yuin/gopher-lua/loadlib.go +++ b/vendor/github.com/yuin/gopher-lua/loadlib.go @@ -65,6 +65,9 @@ func OpenPackage(L *LState) int { L.SetField(packagemod, "path", LString(loGetPath(LuaPath, LuaPathDefault))) L.SetField(packagemod, "cpath", emptyLString) + L.SetField(packagemod, "config", LString(LuaDirSep+"\n"+LuaPathSep+ + "\n"+LuaPathMark+"\n"+LuaExecDir+"\n"+LuaIgMark+"\n")) + L.Push(packagemod) return 1 } diff --git a/vendor/github.com/yuin/gopher-lua/oslib.go b/vendor/github.com/yuin/gopher-lua/oslib.go index c70a99bf13..256c881130 100644 --- a/vendor/github.com/yuin/gopher-lua/oslib.go +++ b/vendor/github.com/yuin/gopher-lua/oslib.go @@ -25,6 +25,9 @@ func getIntField(L *LState, tb *LTable, key string, v int) int { if strings.HasPrefix(slv, "0") && !strings.HasPrefix(slv, "0x") && !strings.HasPrefix(slv, "0X") { //Standard lua interpreter only support decimal and hexadecimal slv = strings.TrimLeft(slv, "0") + if slv == "" { + return 0 + } } if num, err := parseNumber(slv); err == nil { return int(num) @@ -189,20 +192,28 @@ func osTime(L *LState) int { if L.GetTop() == 0 { L.Push(LNumber(time.Now().Unix())) } else { - tbl := L.CheckTable(1) - sec := getIntField(L, tbl, "sec", 0) - min := getIntField(L, tbl, "min", 0) - hour := getIntField(L, tbl, "hour", 12) - day := getIntField(L, tbl, "day", -1) - month := getIntField(L, tbl, "month", -1) - year := getIntField(L, tbl, "year", -1) - isdst := getBoolField(L, tbl, "isdst", false) - t := time.Date(year, time.Month(month), day, hour, min, sec, 0, time.Local) - // TODO dst - if false { - print(isdst) + lv := L.CheckAny(1) + if lv == LNil { + L.Push(LNumber(time.Now().Unix())) + } else { + tbl, ok := lv.(*LTable) + if !ok { + L.TypeError(1, LTTable) + } + sec := getIntField(L, tbl, "sec", 0) + min := getIntField(L, tbl, "min", 0) + hour := getIntField(L, tbl, "hour", 12) + day := getIntField(L, tbl, "day", -1) + month := getIntField(L, tbl, "month", -1) + year := getIntField(L, tbl, "year", -1) + isdst := getBoolField(L, tbl, "isdst", false) + t := time.Date(year, time.Month(month), day, hour, min, sec, 0, time.Local) + // TODO dst + if false { + print(isdst) + } + L.Push(LNumber(t.Unix())) } - L.Push(LNumber(t.Unix())) } return 1 } diff --git a/vendor/github.com/yuin/gopher-lua/parse/Makefile b/vendor/github.com/yuin/gopher-lua/parse/Makefile index 6dd048c165..9838b1393b 100644 --- a/vendor/github.com/yuin/gopher-lua/parse/Makefile +++ b/vendor/github.com/yuin/gopher-lua/parse/Makefile @@ -2,3 +2,6 @@ all : parser.go parser.go : parser.go.y goyacc -o $@ parser.go.y; [ -f y.output ] && ( rm -f y.output ) + +clean: + rm -f parser.go diff --git a/vendor/github.com/yuin/gopher-lua/parse/lexer.go b/vendor/github.com/yuin/gopher-lua/parse/lexer.go index d711e78bc1..6ad57ceed7 100644 --- a/vendor/github.com/yuin/gopher-lua/parse/lexer.go +++ b/vendor/github.com/yuin/gopher-lua/parse/lexer.go @@ -255,7 +255,7 @@ func (sc *Scanner) scanMultilineString(ch int, buf *bytes.Buffer) error { var count1, count2 int count1, ch = sc.countSep(ch) if ch != '[' { - return sc.Error(string(ch), "invalid multiline string") + return sc.Error(string(rune(ch)), "invalid multiline string") } ch = sc.Next() if ch == '\n' || ch == '\r' { @@ -338,7 +338,7 @@ redo: goto redo } else { tok.Type = ch - tok.Str = string(ch) + tok.Str = string(rune(ch)) } case '"', '\'': tok.Type = TString @@ -351,7 +351,7 @@ redo: tok.Str = buf.String() } else { tok.Type = ch - tok.Str = string(ch) + tok.Str = string(rune(ch)) } case '=': if sc.Peek() == '=' { @@ -360,7 +360,7 @@ redo: sc.Next() } else { tok.Type = ch - tok.Str = string(ch) + tok.Str = string(rune(ch)) } case '~': if sc.Peek() == '=' { @@ -377,7 +377,7 @@ redo: sc.Next() } else { tok.Type = ch - tok.Str = string(ch) + tok.Str = string(rune(ch)) } case '>': if sc.Peek() == '=' { @@ -386,7 +386,7 @@ redo: sc.Next() } else { tok.Type = ch - tok.Str = string(ch) + tok.Str = string(rune(ch)) } case '.': ch2 := sc.Peek() @@ -410,7 +410,7 @@ redo: tok.Str = buf.String() case '+', '*', '/', '%', '^', '#', '(', ')', '{', '}', ']', ';', ':', ',': tok.Type = ch - tok.Str = string(ch) + tok.Str = string(rune(ch)) default: writeChar(buf, ch) err = sc.Error(buf.String(), "Invalid token") diff --git a/vendor/github.com/yuin/gopher-lua/parse/parser.go b/vendor/github.com/yuin/gopher-lua/parse/parser.go index f8f59b3615..c756581649 100644 --- a/vendor/github.com/yuin/gopher-lua/parse/parser.go +++ b/vendor/github.com/yuin/gopher-lua/parse/parser.go @@ -1,9 +1,12 @@ +// Code generated by goyacc -o parser.go parser.go.y. DO NOT EDIT. + //line parser.go.y:2 package parse import __yyfmt__ "fmt" //line parser.go.y:2 + import ( "github.com/yuin/gopher-lua/ast" ) @@ -62,7 +65,10 @@ const TNumber = 57374 const TString = 57375 const UNARY = 57376 -var yyToknames = []string{ +var yyToknames = [...]string{ + "$end", + "error", + "$unk", "TAnd", "TBreak", "TDo", @@ -93,25 +99,37 @@ var yyToknames = []string{ "TIdent", "TNumber", "TString", - " {", - " (", - " >", - " <", - " +", - " -", - " *", - " /", - " %", + "'{'", + "'('", + "'>'", + "'<'", + "'+'", + "'-'", + "'*'", + "'/'", + "'%'", "UNARY", - " ^", + "'^'", + "';'", + "'='", + "','", + "':'", + "'.'", + "'['", + "']'", + "'#'", + "')'", + "'}'", } -var yyStatenames = []string{} + +var yyStatenames = [...]string{} const yyEofCode = 1 const yyErrCode = 2 -const yyMaxDepth = 200 +const yyInitialStackSize = 16 + +//line parser.go.y:517 -//line parser.go.y:514 func TokenName(c int) string { if c >= TAnd && c-TAnd < len(yyToknames) { if yyToknames[c-TAnd] != "" { @@ -122,7 +140,7 @@ func TokenName(c int) string { } //line yacctab:1 -var yyExca = []int{ +var yyExca = [...]int8{ -1, 1, 1, -1, -2, 0, @@ -136,16 +154,11 @@ var yyExca = []int{ -2, 68, } -const yyNprod = 95 const yyPrivate = 57344 -var yyTokenNames []string -var yyStates []string - const yyLast = 579 -var yyAct = []int{ - +var yyAct = [...]uint8{ 24, 88, 50, 23, 45, 84, 56, 65, 137, 153, 136, 113, 52, 142, 54, 53, 33, 134, 65, 132, 62, 63, 32, 61, 108, 109, 48, 111, 106, 41, @@ -205,8 +218,8 @@ var yyAct = []int{ 0, 0, 0, 0, 21, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, } -var yyPact = []int{ +var yyPact = [...]int16{ -1000, -1000, 533, -5, -1000, -1000, 292, -1000, -17, 152, -1000, 292, -1000, 292, 107, 97, 88, -1000, -1000, -1000, 292, -1000, -1000, -29, 473, -1000, -1000, -1000, -1000, -1000, @@ -227,14 +240,14 @@ var yyPact = []int{ 311, 151, -1000, 473, 146, 392, -1000, 292, -1000, -1000, -1000, 144, 365, -1000, -1000, -1000, 140, -1000, } -var yyPgo = []int{ +var yyPgo = [...]uint8{ 0, 190, 227, 2, 226, 223, 215, 210, 204, 203, 118, 6, 3, 0, 22, 107, 168, 199, 4, 197, 5, 195, 16, 193, 1, 182, } -var yyR1 = []int{ +var yyR1 = [...]int8{ 0, 1, 1, 1, 2, 2, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 6, 6, 6, 7, 7, 8, @@ -246,8 +259,8 @@ var yyR1 = []int{ 20, 20, 21, 21, 21, 22, 22, 23, 23, 23, 24, 24, 24, 25, 25, } -var yyR2 = []int{ +var yyR2 = [...]int8{ 0, 1, 2, 3, 0, 2, 2, 1, 3, 1, 3, 5, 4, 6, 8, 9, 11, 7, 3, 4, 4, 2, 0, 5, 1, 2, 1, 1, 3, 1, @@ -259,8 +272,8 @@ var yyR2 = []int{ 5, 4, 1, 1, 3, 2, 3, 1, 3, 2, 3, 5, 1, 1, 1, } -var yyChk = []int{ +var yyChk = [...]int16{ -1000, -1, -2, -6, -4, 45, 19, 5, -9, -15, 6, 24, 20, 13, 11, 12, 15, -10, -17, -16, 35, 31, 45, -12, -13, 16, 10, 22, 32, 30, @@ -281,8 +294,8 @@ var yyChk = []int{ -13, -3, 9, -13, -3, -13, 6, 47, 9, 9, 21, -3, -13, -3, 9, 6, -3, 9, } -var yyDef = []int{ +var yyDef = [...]int8{ 4, -2, 1, 2, 5, 6, 24, 26, 0, 9, 4, 0, 4, 0, 0, 0, 0, -2, 69, 70, 0, 33, 3, 25, 38, 40, 41, 42, 43, 44, @@ -303,8 +316,8 @@ var yyDef = []int{ 0, 0, 80, 91, 0, 0, 4, 0, 17, 14, 4, 0, 0, 23, 15, 4, 0, 16, } -var yyTok1 = []int{ +var yyTok1 = [...]int8{ 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, @@ -319,35 +332,63 @@ var yyTok1 = []int{ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 34, 3, 54, } -var yyTok2 = []int{ +var yyTok2 = [...]int8{ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 43, } -var yyTok3 = []int{ + +var yyTok3 = [...]int8{ 0, } +var yyErrorMessages = [...]struct { + state int + token int + msg string +}{} + //line yaccpar:1 /* parser for yacc output */ -var yyDebug = 0 +var ( + yyDebug = 0 + yyErrorVerbose = false +) type yyLexer interface { Lex(lval *yySymType) int Error(s string) } +type yyParser interface { + Parse(yyLexer) int + Lookahead() int +} + +type yyParserImpl struct { + lval yySymType + stack [yyInitialStackSize]yySymType + char int +} + +func (p *yyParserImpl) Lookahead() int { + return p.char +} + +func yyNewParser() yyParser { + return &yyParserImpl{} +} + const yyFlag = -1000 func yyTokname(c int) string { - // 4 is TOKSTART above - if c >= 4 && c-4 < len(yyToknames) { - if yyToknames[c-4] != "" { - return yyToknames[c-4] + if c >= 1 && c-1 < len(yyToknames) { + if yyToknames[c-1] != "" { + return yyToknames[c-1] } } return __yyfmt__.Sprintf("tok-%v", c) @@ -362,51 +403,127 @@ func yyStatname(s int) string { return __yyfmt__.Sprintf("state-%v", s) } -func yylex1(lex yyLexer, lval *yySymType) int { - c := 0 - char := lex.Lex(lval) +func yyErrorMessage(state, lookAhead int) string { + const TOKSTART = 4 + + if !yyErrorVerbose { + return "syntax error" + } + + for _, e := range yyErrorMessages { + if e.state == state && e.token == lookAhead { + return "syntax error: " + e.msg + } + } + + res := "syntax error: unexpected " + yyTokname(lookAhead) + + // To match Bison, suggest at most four expected tokens. + expected := make([]int, 0, 4) + + // Look for shiftable tokens. + base := int(yyPact[state]) + for tok := TOKSTART; tok-1 < len(yyToknames); tok++ { + if n := base + tok; n >= 0 && n < yyLast && int(yyChk[int(yyAct[n])]) == tok { + if len(expected) == cap(expected) { + return res + } + expected = append(expected, tok) + } + } + + if yyDef[state] == -2 { + i := 0 + for yyExca[i] != -1 || int(yyExca[i+1]) != state { + i += 2 + } + + // Look for tokens that we accept or reduce. + for i += 2; yyExca[i] >= 0; i += 2 { + tok := int(yyExca[i]) + if tok < TOKSTART || yyExca[i+1] == 0 { + continue + } + if len(expected) == cap(expected) { + return res + } + expected = append(expected, tok) + } + + // If the default action is to accept or reduce, give up. + if yyExca[i+1] != 0 { + return res + } + } + + for i, tok := range expected { + if i == 0 { + res += ", expecting " + } else { + res += " or " + } + res += yyTokname(tok) + } + return res +} + +func yylex1(lex yyLexer, lval *yySymType) (char, token int) { + token = 0 + char = lex.Lex(lval) if char <= 0 { - c = yyTok1[0] + token = int(yyTok1[0]) goto out } if char < len(yyTok1) { - c = yyTok1[char] + token = int(yyTok1[char]) goto out } if char >= yyPrivate { if char < yyPrivate+len(yyTok2) { - c = yyTok2[char-yyPrivate] + token = int(yyTok2[char-yyPrivate]) goto out } } for i := 0; i < len(yyTok3); i += 2 { - c = yyTok3[i+0] - if c == char { - c = yyTok3[i+1] + token = int(yyTok3[i+0]) + if token == char { + token = int(yyTok3[i+1]) goto out } } out: - if c == 0 { - c = yyTok2[1] /* unknown char */ + if token == 0 { + token = int(yyTok2[1]) /* unknown char */ } if yyDebug >= 3 { - __yyfmt__.Printf("lex %s(%d)\n", yyTokname(c), uint(char)) + __yyfmt__.Printf("lex %s(%d)\n", yyTokname(token), uint(char)) } - return c + return char, token } func yyParse(yylex yyLexer) int { + return yyNewParser().Parse(yylex) +} + +func (yyrcvr *yyParserImpl) Parse(yylex yyLexer) int { var yyn int - var yylval yySymType var yyVAL yySymType - yyS := make([]yySymType, yyMaxDepth) + var yyDollar []yySymType + _ = yyDollar // silence set and not used + yyS := yyrcvr.stack[:] Nerrs := 0 /* number of errors */ Errflag := 0 /* error recovery flag */ yystate := 0 - yychar := -1 + yyrcvr.char = -1 + yytoken := -1 // yyrcvr.char translated into internal numbering + defer func() { + // Make sure we report no lookahead when not parsing. + yystate = -1 + yyrcvr.char = -1 + yytoken = -1 + }() yyp := -1 goto yystack @@ -419,7 +536,7 @@ ret1: yystack: /* put a state and value onto the stack */ if yyDebug >= 4 { - __yyfmt__.Printf("char %v in %v\n", yyTokname(yychar), yyStatname(yystate)) + __yyfmt__.Printf("char %v in %v\n", yyTokname(yytoken), yyStatname(yystate)) } yyp++ @@ -432,21 +549,22 @@ yystack: yyS[yyp].yys = yystate yynewstate: - yyn = yyPact[yystate] + yyn = int(yyPact[yystate]) if yyn <= yyFlag { goto yydefault /* simple state */ } - if yychar < 0 { - yychar = yylex1(yylex, &yylval) + if yyrcvr.char < 0 { + yyrcvr.char, yytoken = yylex1(yylex, &yyrcvr.lval) } - yyn += yychar + yyn += yytoken if yyn < 0 || yyn >= yyLast { goto yydefault } - yyn = yyAct[yyn] - if yyChk[yyn] == yychar { /* valid shift */ - yychar = -1 - yyVAL = yylval + yyn = int(yyAct[yyn]) + if int(yyChk[yyn]) == yytoken { /* valid shift */ + yyrcvr.char = -1 + yytoken = -1 + yyVAL = yyrcvr.lval yystate = yyn if Errflag > 0 { Errflag-- @@ -456,27 +574,27 @@ yynewstate: yydefault: /* default state action */ - yyn = yyDef[yystate] + yyn = int(yyDef[yystate]) if yyn == -2 { - if yychar < 0 { - yychar = yylex1(yylex, &yylval) + if yyrcvr.char < 0 { + yyrcvr.char, yytoken = yylex1(yylex, &yyrcvr.lval) } /* look through exception table */ xi := 0 for { - if yyExca[xi+0] == -1 && yyExca[xi+1] == yystate { + if yyExca[xi+0] == -1 && int(yyExca[xi+1]) == yystate { break } xi += 2 } for xi += 2; ; xi += 2 { - yyn = yyExca[xi+0] - if yyn < 0 || yyn == yychar { + yyn = int(yyExca[xi+0]) + if yyn < 0 || yyn == yytoken { break } } - yyn = yyExca[xi+1] + yyn = int(yyExca[xi+1]) if yyn < 0 { goto ret0 } @@ -485,11 +603,11 @@ yydefault: /* error ... attempt to resume parsing */ switch Errflag { case 0: /* brand new error */ - yylex.Error("syntax error") + yylex.Error(yyErrorMessage(yystate, yytoken)) Nerrs++ if yyDebug >= 1 { __yyfmt__.Printf("%s", yyStatname(yystate)) - __yyfmt__.Printf(" saw %s\n", yyTokname(yychar)) + __yyfmt__.Printf(" saw %s\n", yyTokname(yytoken)) } fallthrough @@ -498,10 +616,10 @@ yydefault: /* find a state where "error" is a legal shift action */ for yyp >= 0 { - yyn = yyPact[yyS[yyp].yys] + yyErrCode + yyn = int(yyPact[yyS[yyp].yys]) + yyErrCode if yyn >= 0 && yyn < yyLast { - yystate = yyAct[yyn] /* simulate a shift of "error" */ - if yyChk[yystate] == yyErrCode { + yystate = int(yyAct[yyn]) /* simulate a shift of "error" */ + if int(yyChk[yystate]) == yyErrCode { goto yystack } } @@ -517,12 +635,13 @@ yydefault: case 3: /* no shift yet; clobber input char */ if yyDebug >= 2 { - __yyfmt__.Printf("error recovery discards %s\n", yyTokname(yychar)) + __yyfmt__.Printf("error recovery discards %s\n", yyTokname(yytoken)) } - if yychar == yyEofCode { + if yytoken == yyEofCode { goto ret1 } - yychar = -1 + yyrcvr.char = -1 + yytoken = -1 goto yynewstate /* try again in the same state */ } } @@ -536,599 +655,703 @@ yydefault: yypt := yyp _ = yypt // guard against "declared and not used" - yyp -= yyR2[yyn] + yyp -= int(yyR2[yyn]) + // yyp is now the index of $0. Perform the default action. Iff the + // reduced production is ε, $1 is possibly out of range. + if yyp+1 >= len(yyS) { + nyys := make([]yySymType, len(yyS)*2) + copy(nyys, yyS) + yyS = nyys + } yyVAL = yyS[yyp+1] /* consult goto table to find next state */ - yyn = yyR1[yyn] - yyg := yyPgo[yyn] + yyn = int(yyR1[yyn]) + yyg := int(yyPgo[yyn]) yyj := yyg + yyS[yyp].yys + 1 if yyj >= yyLast { - yystate = yyAct[yyg] + yystate = int(yyAct[yyg]) } else { - yystate = yyAct[yyj] - if yyChk[yystate] != -yyn { - yystate = yyAct[yyg] + yystate = int(yyAct[yyj]) + if int(yyChk[yystate]) != -yyn { + yystate = int(yyAct[yyg]) } } // dummy call; replaced with literal code switch yynt { case 1: - //line parser.go.y:73 + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:73 { - yyVAL.stmts = yyS[yypt-0].stmts + yyVAL.stmts = yyDollar[1].stmts if l, ok := yylex.(*Lexer); ok { l.Stmts = yyVAL.stmts } } case 2: - //line parser.go.y:79 + yyDollar = yyS[yypt-2 : yypt+1] +//line parser.go.y:79 { - yyVAL.stmts = append(yyS[yypt-1].stmts, yyS[yypt-0].stmt) + yyVAL.stmts = append(yyDollar[1].stmts, yyDollar[2].stmt) if l, ok := yylex.(*Lexer); ok { l.Stmts = yyVAL.stmts } } case 3: - //line parser.go.y:85 + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:85 { - yyVAL.stmts = append(yyS[yypt-2].stmts, yyS[yypt-1].stmt) + yyVAL.stmts = append(yyDollar[1].stmts, yyDollar[2].stmt) if l, ok := yylex.(*Lexer); ok { l.Stmts = yyVAL.stmts } } case 4: - //line parser.go.y:93 + yyDollar = yyS[yypt-0 : yypt+1] +//line parser.go.y:93 { yyVAL.stmts = []ast.Stmt{} } case 5: - //line parser.go.y:96 + yyDollar = yyS[yypt-2 : yypt+1] +//line parser.go.y:96 { - yyVAL.stmts = append(yyS[yypt-1].stmts, yyS[yypt-0].stmt) + yyVAL.stmts = append(yyDollar[1].stmts, yyDollar[2].stmt) } case 6: - //line parser.go.y:99 + yyDollar = yyS[yypt-2 : yypt+1] +//line parser.go.y:99 { - yyVAL.stmts = yyS[yypt-1].stmts + yyVAL.stmts = yyDollar[1].stmts } case 7: - //line parser.go.y:104 + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:104 { - yyVAL.stmts = yyS[yypt-0].stmts + yyVAL.stmts = yyDollar[1].stmts } case 8: - //line parser.go.y:109 + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:109 { - yyVAL.stmt = &ast.AssignStmt{Lhs: yyS[yypt-2].exprlist, Rhs: yyS[yypt-0].exprlist} - yyVAL.stmt.SetLine(yyS[yypt-2].exprlist[0].Line()) + yyVAL.stmt = &ast.AssignStmt{Lhs: yyDollar[1].exprlist, Rhs: yyDollar[3].exprlist} + yyVAL.stmt.SetLine(yyDollar[1].exprlist[0].Line()) } case 9: - //line parser.go.y:114 + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:114 { - if _, ok := yyS[yypt-0].expr.(*ast.FuncCallExpr); !ok { + if _, ok := yyDollar[1].expr.(*ast.FuncCallExpr); !ok { yylex.(*Lexer).Error("parse error") } else { - yyVAL.stmt = &ast.FuncCallStmt{Expr: yyS[yypt-0].expr} - yyVAL.stmt.SetLine(yyS[yypt-0].expr.Line()) + yyVAL.stmt = &ast.FuncCallStmt{Expr: yyDollar[1].expr} + yyVAL.stmt.SetLine(yyDollar[1].expr.Line()) } } case 10: - //line parser.go.y:122 + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:122 { - yyVAL.stmt = &ast.DoBlockStmt{Stmts: yyS[yypt-1].stmts} - yyVAL.stmt.SetLine(yyS[yypt-2].token.Pos.Line) - yyVAL.stmt.SetLastLine(yyS[yypt-0].token.Pos.Line) + yyVAL.stmt = &ast.DoBlockStmt{Stmts: yyDollar[2].stmts} + yyVAL.stmt.SetLine(yyDollar[1].token.Pos.Line) + yyVAL.stmt.SetLastLine(yyDollar[3].token.Pos.Line) } case 11: - //line parser.go.y:127 + yyDollar = yyS[yypt-5 : yypt+1] +//line parser.go.y:127 { - yyVAL.stmt = &ast.WhileStmt{Condition: yyS[yypt-3].expr, Stmts: yyS[yypt-1].stmts} - yyVAL.stmt.SetLine(yyS[yypt-4].token.Pos.Line) - yyVAL.stmt.SetLastLine(yyS[yypt-0].token.Pos.Line) + yyVAL.stmt = &ast.WhileStmt{Condition: yyDollar[2].expr, Stmts: yyDollar[4].stmts} + yyVAL.stmt.SetLine(yyDollar[1].token.Pos.Line) + yyVAL.stmt.SetLastLine(yyDollar[5].token.Pos.Line) } case 12: - //line parser.go.y:132 + yyDollar = yyS[yypt-4 : yypt+1] +//line parser.go.y:132 { - yyVAL.stmt = &ast.RepeatStmt{Condition: yyS[yypt-0].expr, Stmts: yyS[yypt-2].stmts} - yyVAL.stmt.SetLine(yyS[yypt-3].token.Pos.Line) - yyVAL.stmt.SetLastLine(yyS[yypt-0].expr.Line()) + yyVAL.stmt = &ast.RepeatStmt{Condition: yyDollar[4].expr, Stmts: yyDollar[2].stmts} + yyVAL.stmt.SetLine(yyDollar[1].token.Pos.Line) + yyVAL.stmt.SetLastLine(yyDollar[4].expr.Line()) } case 13: - //line parser.go.y:137 + yyDollar = yyS[yypt-6 : yypt+1] +//line parser.go.y:137 { - yyVAL.stmt = &ast.IfStmt{Condition: yyS[yypt-4].expr, Then: yyS[yypt-2].stmts} + yyVAL.stmt = &ast.IfStmt{Condition: yyDollar[2].expr, Then: yyDollar[4].stmts} cur := yyVAL.stmt - for _, elseif := range yyS[yypt-1].stmts { + for _, elseif := range yyDollar[5].stmts { cur.(*ast.IfStmt).Else = []ast.Stmt{elseif} cur = elseif } - yyVAL.stmt.SetLine(yyS[yypt-5].token.Pos.Line) - yyVAL.stmt.SetLastLine(yyS[yypt-0].token.Pos.Line) + yyVAL.stmt.SetLine(yyDollar[1].token.Pos.Line) + yyVAL.stmt.SetLastLine(yyDollar[6].token.Pos.Line) } case 14: - //line parser.go.y:147 + yyDollar = yyS[yypt-8 : yypt+1] +//line parser.go.y:147 { - yyVAL.stmt = &ast.IfStmt{Condition: yyS[yypt-6].expr, Then: yyS[yypt-4].stmts} + yyVAL.stmt = &ast.IfStmt{Condition: yyDollar[2].expr, Then: yyDollar[4].stmts} cur := yyVAL.stmt - for _, elseif := range yyS[yypt-3].stmts { + for _, elseif := range yyDollar[5].stmts { cur.(*ast.IfStmt).Else = []ast.Stmt{elseif} cur = elseif } - cur.(*ast.IfStmt).Else = yyS[yypt-1].stmts - yyVAL.stmt.SetLine(yyS[yypt-7].token.Pos.Line) - yyVAL.stmt.SetLastLine(yyS[yypt-0].token.Pos.Line) + cur.(*ast.IfStmt).Else = yyDollar[7].stmts + yyVAL.stmt.SetLine(yyDollar[1].token.Pos.Line) + yyVAL.stmt.SetLastLine(yyDollar[8].token.Pos.Line) } case 15: - //line parser.go.y:158 + yyDollar = yyS[yypt-9 : yypt+1] +//line parser.go.y:158 { - yyVAL.stmt = &ast.NumberForStmt{Name: yyS[yypt-7].token.Str, Init: yyS[yypt-5].expr, Limit: yyS[yypt-3].expr, Stmts: yyS[yypt-1].stmts} - yyVAL.stmt.SetLine(yyS[yypt-8].token.Pos.Line) - yyVAL.stmt.SetLastLine(yyS[yypt-0].token.Pos.Line) + yyVAL.stmt = &ast.NumberForStmt{Name: yyDollar[2].token.Str, Init: yyDollar[4].expr, Limit: yyDollar[6].expr, Stmts: yyDollar[8].stmts} + yyVAL.stmt.SetLine(yyDollar[1].token.Pos.Line) + yyVAL.stmt.SetLastLine(yyDollar[9].token.Pos.Line) } case 16: - //line parser.go.y:163 + yyDollar = yyS[yypt-11 : yypt+1] +//line parser.go.y:163 { - yyVAL.stmt = &ast.NumberForStmt{Name: yyS[yypt-9].token.Str, Init: yyS[yypt-7].expr, Limit: yyS[yypt-5].expr, Step: yyS[yypt-3].expr, Stmts: yyS[yypt-1].stmts} - yyVAL.stmt.SetLine(yyS[yypt-10].token.Pos.Line) - yyVAL.stmt.SetLastLine(yyS[yypt-0].token.Pos.Line) + yyVAL.stmt = &ast.NumberForStmt{Name: yyDollar[2].token.Str, Init: yyDollar[4].expr, Limit: yyDollar[6].expr, Step: yyDollar[8].expr, Stmts: yyDollar[10].stmts} + yyVAL.stmt.SetLine(yyDollar[1].token.Pos.Line) + yyVAL.stmt.SetLastLine(yyDollar[11].token.Pos.Line) } case 17: - //line parser.go.y:168 + yyDollar = yyS[yypt-7 : yypt+1] +//line parser.go.y:168 { - yyVAL.stmt = &ast.GenericForStmt{Names: yyS[yypt-5].namelist, Exprs: yyS[yypt-3].exprlist, Stmts: yyS[yypt-1].stmts} - yyVAL.stmt.SetLine(yyS[yypt-6].token.Pos.Line) - yyVAL.stmt.SetLastLine(yyS[yypt-0].token.Pos.Line) + yyVAL.stmt = &ast.GenericForStmt{Names: yyDollar[2].namelist, Exprs: yyDollar[4].exprlist, Stmts: yyDollar[6].stmts} + yyVAL.stmt.SetLine(yyDollar[1].token.Pos.Line) + yyVAL.stmt.SetLastLine(yyDollar[7].token.Pos.Line) } case 18: - //line parser.go.y:173 + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:173 { - yyVAL.stmt = &ast.FuncDefStmt{Name: yyS[yypt-1].funcname, Func: yyS[yypt-0].funcexpr} - yyVAL.stmt.SetLine(yyS[yypt-2].token.Pos.Line) - yyVAL.stmt.SetLastLine(yyS[yypt-0].funcexpr.LastLine()) + yyVAL.stmt = &ast.FuncDefStmt{Name: yyDollar[2].funcname, Func: yyDollar[3].funcexpr} + yyVAL.stmt.SetLine(yyDollar[1].token.Pos.Line) + yyVAL.stmt.SetLastLine(yyDollar[3].funcexpr.LastLine()) } case 19: - //line parser.go.y:178 + yyDollar = yyS[yypt-4 : yypt+1] +//line parser.go.y:178 { - yyVAL.stmt = &ast.LocalAssignStmt{Names: []string{yyS[yypt-1].token.Str}, Exprs: []ast.Expr{yyS[yypt-0].funcexpr}} - yyVAL.stmt.SetLine(yyS[yypt-3].token.Pos.Line) - yyVAL.stmt.SetLastLine(yyS[yypt-0].funcexpr.LastLine()) + yyVAL.stmt = &ast.LocalAssignStmt{Names: []string{yyDollar[3].token.Str}, Exprs: []ast.Expr{yyDollar[4].funcexpr}} + yyVAL.stmt.SetLine(yyDollar[1].token.Pos.Line) + yyVAL.stmt.SetLastLine(yyDollar[4].funcexpr.LastLine()) } case 20: - //line parser.go.y:183 + yyDollar = yyS[yypt-4 : yypt+1] +//line parser.go.y:183 { - yyVAL.stmt = &ast.LocalAssignStmt{Names: yyS[yypt-2].namelist, Exprs: yyS[yypt-0].exprlist} - yyVAL.stmt.SetLine(yyS[yypt-3].token.Pos.Line) + yyVAL.stmt = &ast.LocalAssignStmt{Names: yyDollar[2].namelist, Exprs: yyDollar[4].exprlist} + yyVAL.stmt.SetLine(yyDollar[1].token.Pos.Line) } case 21: - //line parser.go.y:187 + yyDollar = yyS[yypt-2 : yypt+1] +//line parser.go.y:187 { - yyVAL.stmt = &ast.LocalAssignStmt{Names: yyS[yypt-0].namelist, Exprs: []ast.Expr{}} - yyVAL.stmt.SetLine(yyS[yypt-1].token.Pos.Line) + yyVAL.stmt = &ast.LocalAssignStmt{Names: yyDollar[2].namelist, Exprs: []ast.Expr{}} + yyVAL.stmt.SetLine(yyDollar[1].token.Pos.Line) } case 22: - //line parser.go.y:193 + yyDollar = yyS[yypt-0 : yypt+1] +//line parser.go.y:193 { yyVAL.stmts = []ast.Stmt{} } case 23: - //line parser.go.y:196 + yyDollar = yyS[yypt-5 : yypt+1] +//line parser.go.y:196 { - yyVAL.stmts = append(yyS[yypt-4].stmts, &ast.IfStmt{Condition: yyS[yypt-2].expr, Then: yyS[yypt-0].stmts}) - yyVAL.stmts[len(yyVAL.stmts)-1].SetLine(yyS[yypt-3].token.Pos.Line) + yyVAL.stmts = append(yyDollar[1].stmts, &ast.IfStmt{Condition: yyDollar[3].expr, Then: yyDollar[5].stmts}) + yyVAL.stmts[len(yyVAL.stmts)-1].SetLine(yyDollar[2].token.Pos.Line) } case 24: - //line parser.go.y:202 + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:202 { yyVAL.stmt = &ast.ReturnStmt{Exprs: nil} - yyVAL.stmt.SetLine(yyS[yypt-0].token.Pos.Line) + yyVAL.stmt.SetLine(yyDollar[1].token.Pos.Line) } case 25: - //line parser.go.y:206 + yyDollar = yyS[yypt-2 : yypt+1] +//line parser.go.y:206 { - yyVAL.stmt = &ast.ReturnStmt{Exprs: yyS[yypt-0].exprlist} - yyVAL.stmt.SetLine(yyS[yypt-1].token.Pos.Line) + yyVAL.stmt = &ast.ReturnStmt{Exprs: yyDollar[2].exprlist} + yyVAL.stmt.SetLine(yyDollar[1].token.Pos.Line) } case 26: - //line parser.go.y:210 + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:210 { yyVAL.stmt = &ast.BreakStmt{} - yyVAL.stmt.SetLine(yyS[yypt-0].token.Pos.Line) + yyVAL.stmt.SetLine(yyDollar[1].token.Pos.Line) } case 27: - //line parser.go.y:216 + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:216 { - yyVAL.funcname = yyS[yypt-0].funcname + yyVAL.funcname = yyDollar[1].funcname } case 28: - //line parser.go.y:219 + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:219 { - yyVAL.funcname = &ast.FuncName{Func: nil, Receiver: yyS[yypt-2].funcname.Func, Method: yyS[yypt-0].token.Str} + yyVAL.funcname = &ast.FuncName{Func: nil, Receiver: yyDollar[1].funcname.Func, Method: yyDollar[3].token.Str} } case 29: - //line parser.go.y:224 + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:224 { - yyVAL.funcname = &ast.FuncName{Func: &ast.IdentExpr{Value: yyS[yypt-0].token.Str}} - yyVAL.funcname.Func.SetLine(yyS[yypt-0].token.Pos.Line) + yyVAL.funcname = &ast.FuncName{Func: &ast.IdentExpr{Value: yyDollar[1].token.Str}} + yyVAL.funcname.Func.SetLine(yyDollar[1].token.Pos.Line) } case 30: - //line parser.go.y:228 + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:228 { - key := &ast.StringExpr{Value: yyS[yypt-0].token.Str} - key.SetLine(yyS[yypt-0].token.Pos.Line) - fn := &ast.AttrGetExpr{Object: yyS[yypt-2].funcname.Func, Key: key} - fn.SetLine(yyS[yypt-0].token.Pos.Line) + key := &ast.StringExpr{Value: yyDollar[3].token.Str} + key.SetLine(yyDollar[3].token.Pos.Line) + fn := &ast.AttrGetExpr{Object: yyDollar[1].funcname.Func, Key: key} + fn.SetLine(yyDollar[3].token.Pos.Line) yyVAL.funcname = &ast.FuncName{Func: fn} } case 31: - //line parser.go.y:237 + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:237 { - yyVAL.exprlist = []ast.Expr{yyS[yypt-0].expr} + yyVAL.exprlist = []ast.Expr{yyDollar[1].expr} } case 32: - //line parser.go.y:240 + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:240 { - yyVAL.exprlist = append(yyS[yypt-2].exprlist, yyS[yypt-0].expr) + yyVAL.exprlist = append(yyDollar[1].exprlist, yyDollar[3].expr) } case 33: - //line parser.go.y:245 + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:245 { - yyVAL.expr = &ast.IdentExpr{Value: yyS[yypt-0].token.Str} - yyVAL.expr.SetLine(yyS[yypt-0].token.Pos.Line) + yyVAL.expr = &ast.IdentExpr{Value: yyDollar[1].token.Str} + yyVAL.expr.SetLine(yyDollar[1].token.Pos.Line) } case 34: - //line parser.go.y:249 + yyDollar = yyS[yypt-4 : yypt+1] +//line parser.go.y:249 { - yyVAL.expr = &ast.AttrGetExpr{Object: yyS[yypt-3].expr, Key: yyS[yypt-1].expr} - yyVAL.expr.SetLine(yyS[yypt-3].expr.Line()) + yyVAL.expr = &ast.AttrGetExpr{Object: yyDollar[1].expr, Key: yyDollar[3].expr} + yyVAL.expr.SetLine(yyDollar[1].expr.Line()) } case 35: - //line parser.go.y:253 + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:253 { - key := &ast.StringExpr{Value: yyS[yypt-0].token.Str} - key.SetLine(yyS[yypt-0].token.Pos.Line) - yyVAL.expr = &ast.AttrGetExpr{Object: yyS[yypt-2].expr, Key: key} - yyVAL.expr.SetLine(yyS[yypt-2].expr.Line()) + key := &ast.StringExpr{Value: yyDollar[3].token.Str} + key.SetLine(yyDollar[3].token.Pos.Line) + yyVAL.expr = &ast.AttrGetExpr{Object: yyDollar[1].expr, Key: key} + yyVAL.expr.SetLine(yyDollar[1].expr.Line()) } case 36: - //line parser.go.y:261 + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:261 { - yyVAL.namelist = []string{yyS[yypt-0].token.Str} + yyVAL.namelist = []string{yyDollar[1].token.Str} } case 37: - //line parser.go.y:264 + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:264 { - yyVAL.namelist = append(yyS[yypt-2].namelist, yyS[yypt-0].token.Str) + yyVAL.namelist = append(yyDollar[1].namelist, yyDollar[3].token.Str) } case 38: - //line parser.go.y:269 + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:269 { - yyVAL.exprlist = []ast.Expr{yyS[yypt-0].expr} + yyVAL.exprlist = []ast.Expr{yyDollar[1].expr} } case 39: - //line parser.go.y:272 + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:272 { - yyVAL.exprlist = append(yyS[yypt-2].exprlist, yyS[yypt-0].expr) + yyVAL.exprlist = append(yyDollar[1].exprlist, yyDollar[3].expr) } case 40: - //line parser.go.y:277 + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:277 { yyVAL.expr = &ast.NilExpr{} - yyVAL.expr.SetLine(yyS[yypt-0].token.Pos.Line) + yyVAL.expr.SetLine(yyDollar[1].token.Pos.Line) } case 41: - //line parser.go.y:281 + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:281 { yyVAL.expr = &ast.FalseExpr{} - yyVAL.expr.SetLine(yyS[yypt-0].token.Pos.Line) + yyVAL.expr.SetLine(yyDollar[1].token.Pos.Line) } case 42: - //line parser.go.y:285 + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:285 { yyVAL.expr = &ast.TrueExpr{} - yyVAL.expr.SetLine(yyS[yypt-0].token.Pos.Line) + yyVAL.expr.SetLine(yyDollar[1].token.Pos.Line) } case 43: - //line parser.go.y:289 + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:289 { - yyVAL.expr = &ast.NumberExpr{Value: yyS[yypt-0].token.Str} - yyVAL.expr.SetLine(yyS[yypt-0].token.Pos.Line) + yyVAL.expr = &ast.NumberExpr{Value: yyDollar[1].token.Str} + yyVAL.expr.SetLine(yyDollar[1].token.Pos.Line) } case 44: - //line parser.go.y:293 + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:293 { yyVAL.expr = &ast.Comma3Expr{} - yyVAL.expr.SetLine(yyS[yypt-0].token.Pos.Line) + yyVAL.expr.SetLine(yyDollar[1].token.Pos.Line) } case 45: - //line parser.go.y:297 + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:297 { - yyVAL.expr = yyS[yypt-0].expr + yyVAL.expr = yyDollar[1].expr } case 46: - //line parser.go.y:300 + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:300 { - yyVAL.expr = yyS[yypt-0].expr + yyVAL.expr = yyDollar[1].expr } case 47: - //line parser.go.y:303 + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:303 { - yyVAL.expr = yyS[yypt-0].expr + yyVAL.expr = yyDollar[1].expr } case 48: - //line parser.go.y:306 + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:306 { - yyVAL.expr = yyS[yypt-0].expr + yyVAL.expr = yyDollar[1].expr } case 49: - //line parser.go.y:309 + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:309 { - yyVAL.expr = &ast.LogicalOpExpr{Lhs: yyS[yypt-2].expr, Operator: "or", Rhs: yyS[yypt-0].expr} - yyVAL.expr.SetLine(yyS[yypt-2].expr.Line()) + yyVAL.expr = &ast.LogicalOpExpr{Lhs: yyDollar[1].expr, Operator: "or", Rhs: yyDollar[3].expr} + yyVAL.expr.SetLine(yyDollar[1].expr.Line()) } case 50: - //line parser.go.y:313 + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:313 { - yyVAL.expr = &ast.LogicalOpExpr{Lhs: yyS[yypt-2].expr, Operator: "and", Rhs: yyS[yypt-0].expr} - yyVAL.expr.SetLine(yyS[yypt-2].expr.Line()) + yyVAL.expr = &ast.LogicalOpExpr{Lhs: yyDollar[1].expr, Operator: "and", Rhs: yyDollar[3].expr} + yyVAL.expr.SetLine(yyDollar[1].expr.Line()) } case 51: - //line parser.go.y:317 + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:317 { - yyVAL.expr = &ast.RelationalOpExpr{Lhs: yyS[yypt-2].expr, Operator: ">", Rhs: yyS[yypt-0].expr} - yyVAL.expr.SetLine(yyS[yypt-2].expr.Line()) + yyVAL.expr = &ast.RelationalOpExpr{Lhs: yyDollar[1].expr, Operator: ">", Rhs: yyDollar[3].expr} + yyVAL.expr.SetLine(yyDollar[1].expr.Line()) } case 52: - //line parser.go.y:321 + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:321 { - yyVAL.expr = &ast.RelationalOpExpr{Lhs: yyS[yypt-2].expr, Operator: "<", Rhs: yyS[yypt-0].expr} - yyVAL.expr.SetLine(yyS[yypt-2].expr.Line()) + yyVAL.expr = &ast.RelationalOpExpr{Lhs: yyDollar[1].expr, Operator: "<", Rhs: yyDollar[3].expr} + yyVAL.expr.SetLine(yyDollar[1].expr.Line()) } case 53: - //line parser.go.y:325 + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:325 { - yyVAL.expr = &ast.RelationalOpExpr{Lhs: yyS[yypt-2].expr, Operator: ">=", Rhs: yyS[yypt-0].expr} - yyVAL.expr.SetLine(yyS[yypt-2].expr.Line()) + yyVAL.expr = &ast.RelationalOpExpr{Lhs: yyDollar[1].expr, Operator: ">=", Rhs: yyDollar[3].expr} + yyVAL.expr.SetLine(yyDollar[1].expr.Line()) } case 54: - //line parser.go.y:329 + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:329 { - yyVAL.expr = &ast.RelationalOpExpr{Lhs: yyS[yypt-2].expr, Operator: "<=", Rhs: yyS[yypt-0].expr} - yyVAL.expr.SetLine(yyS[yypt-2].expr.Line()) + yyVAL.expr = &ast.RelationalOpExpr{Lhs: yyDollar[1].expr, Operator: "<=", Rhs: yyDollar[3].expr} + yyVAL.expr.SetLine(yyDollar[1].expr.Line()) } case 55: - //line parser.go.y:333 + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:333 { - yyVAL.expr = &ast.RelationalOpExpr{Lhs: yyS[yypt-2].expr, Operator: "==", Rhs: yyS[yypt-0].expr} - yyVAL.expr.SetLine(yyS[yypt-2].expr.Line()) + yyVAL.expr = &ast.RelationalOpExpr{Lhs: yyDollar[1].expr, Operator: "==", Rhs: yyDollar[3].expr} + yyVAL.expr.SetLine(yyDollar[1].expr.Line()) } case 56: - //line parser.go.y:337 + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:337 { - yyVAL.expr = &ast.RelationalOpExpr{Lhs: yyS[yypt-2].expr, Operator: "~=", Rhs: yyS[yypt-0].expr} - yyVAL.expr.SetLine(yyS[yypt-2].expr.Line()) + yyVAL.expr = &ast.RelationalOpExpr{Lhs: yyDollar[1].expr, Operator: "~=", Rhs: yyDollar[3].expr} + yyVAL.expr.SetLine(yyDollar[1].expr.Line()) } case 57: - //line parser.go.y:341 + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:341 { - yyVAL.expr = &ast.StringConcatOpExpr{Lhs: yyS[yypt-2].expr, Rhs: yyS[yypt-0].expr} - yyVAL.expr.SetLine(yyS[yypt-2].expr.Line()) + yyVAL.expr = &ast.StringConcatOpExpr{Lhs: yyDollar[1].expr, Rhs: yyDollar[3].expr} + yyVAL.expr.SetLine(yyDollar[1].expr.Line()) } case 58: - //line parser.go.y:345 + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:345 { - yyVAL.expr = &ast.ArithmeticOpExpr{Lhs: yyS[yypt-2].expr, Operator: "+", Rhs: yyS[yypt-0].expr} - yyVAL.expr.SetLine(yyS[yypt-2].expr.Line()) + yyVAL.expr = &ast.ArithmeticOpExpr{Lhs: yyDollar[1].expr, Operator: "+", Rhs: yyDollar[3].expr} + yyVAL.expr.SetLine(yyDollar[1].expr.Line()) } case 59: - //line parser.go.y:349 + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:349 { - yyVAL.expr = &ast.ArithmeticOpExpr{Lhs: yyS[yypt-2].expr, Operator: "-", Rhs: yyS[yypt-0].expr} - yyVAL.expr.SetLine(yyS[yypt-2].expr.Line()) + yyVAL.expr = &ast.ArithmeticOpExpr{Lhs: yyDollar[1].expr, Operator: "-", Rhs: yyDollar[3].expr} + yyVAL.expr.SetLine(yyDollar[1].expr.Line()) } case 60: - //line parser.go.y:353 + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:353 { - yyVAL.expr = &ast.ArithmeticOpExpr{Lhs: yyS[yypt-2].expr, Operator: "*", Rhs: yyS[yypt-0].expr} - yyVAL.expr.SetLine(yyS[yypt-2].expr.Line()) + yyVAL.expr = &ast.ArithmeticOpExpr{Lhs: yyDollar[1].expr, Operator: "*", Rhs: yyDollar[3].expr} + yyVAL.expr.SetLine(yyDollar[1].expr.Line()) } case 61: - //line parser.go.y:357 + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:357 { - yyVAL.expr = &ast.ArithmeticOpExpr{Lhs: yyS[yypt-2].expr, Operator: "/", Rhs: yyS[yypt-0].expr} - yyVAL.expr.SetLine(yyS[yypt-2].expr.Line()) + yyVAL.expr = &ast.ArithmeticOpExpr{Lhs: yyDollar[1].expr, Operator: "/", Rhs: yyDollar[3].expr} + yyVAL.expr.SetLine(yyDollar[1].expr.Line()) } case 62: - //line parser.go.y:361 + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:361 { - yyVAL.expr = &ast.ArithmeticOpExpr{Lhs: yyS[yypt-2].expr, Operator: "%", Rhs: yyS[yypt-0].expr} - yyVAL.expr.SetLine(yyS[yypt-2].expr.Line()) + yyVAL.expr = &ast.ArithmeticOpExpr{Lhs: yyDollar[1].expr, Operator: "%", Rhs: yyDollar[3].expr} + yyVAL.expr.SetLine(yyDollar[1].expr.Line()) } case 63: - //line parser.go.y:365 + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:365 { - yyVAL.expr = &ast.ArithmeticOpExpr{Lhs: yyS[yypt-2].expr, Operator: "^", Rhs: yyS[yypt-0].expr} - yyVAL.expr.SetLine(yyS[yypt-2].expr.Line()) + yyVAL.expr = &ast.ArithmeticOpExpr{Lhs: yyDollar[1].expr, Operator: "^", Rhs: yyDollar[3].expr} + yyVAL.expr.SetLine(yyDollar[1].expr.Line()) } case 64: - //line parser.go.y:369 + yyDollar = yyS[yypt-2 : yypt+1] +//line parser.go.y:369 { - yyVAL.expr = &ast.UnaryMinusOpExpr{Expr: yyS[yypt-0].expr} - yyVAL.expr.SetLine(yyS[yypt-0].expr.Line()) + yyVAL.expr = &ast.UnaryMinusOpExpr{Expr: yyDollar[2].expr} + yyVAL.expr.SetLine(yyDollar[2].expr.Line()) } case 65: - //line parser.go.y:373 + yyDollar = yyS[yypt-2 : yypt+1] +//line parser.go.y:373 { - yyVAL.expr = &ast.UnaryNotOpExpr{Expr: yyS[yypt-0].expr} - yyVAL.expr.SetLine(yyS[yypt-0].expr.Line()) + yyVAL.expr = &ast.UnaryNotOpExpr{Expr: yyDollar[2].expr} + yyVAL.expr.SetLine(yyDollar[2].expr.Line()) } case 66: - //line parser.go.y:377 + yyDollar = yyS[yypt-2 : yypt+1] +//line parser.go.y:377 { - yyVAL.expr = &ast.UnaryLenOpExpr{Expr: yyS[yypt-0].expr} - yyVAL.expr.SetLine(yyS[yypt-0].expr.Line()) + yyVAL.expr = &ast.UnaryLenOpExpr{Expr: yyDollar[2].expr} + yyVAL.expr.SetLine(yyDollar[2].expr.Line()) } case 67: - //line parser.go.y:383 + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:383 { - yyVAL.expr = &ast.StringExpr{Value: yyS[yypt-0].token.Str} - yyVAL.expr.SetLine(yyS[yypt-0].token.Pos.Line) + yyVAL.expr = &ast.StringExpr{Value: yyDollar[1].token.Str} + yyVAL.expr.SetLine(yyDollar[1].token.Pos.Line) } case 68: - //line parser.go.y:389 + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:389 { - yyVAL.expr = yyS[yypt-0].expr + yyVAL.expr = yyDollar[1].expr } case 69: - //line parser.go.y:392 + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:392 { - yyVAL.expr = yyS[yypt-0].expr + yyVAL.expr = yyDollar[1].expr } case 70: - //line parser.go.y:395 + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:395 { - yyVAL.expr = yyS[yypt-0].expr + yyVAL.expr = yyDollar[1].expr } case 71: - //line parser.go.y:398 + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:398 { - yyVAL.expr = yyS[yypt-1].expr - yyVAL.expr.SetLine(yyS[yypt-2].token.Pos.Line) + if ex, ok := yyDollar[2].expr.(*ast.Comma3Expr); ok { + ex.AdjustRet = true + } + yyVAL.expr = yyDollar[2].expr + yyVAL.expr.SetLine(yyDollar[1].token.Pos.Line) } case 72: - //line parser.go.y:404 + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:407 { - yyS[yypt-1].expr.(*ast.FuncCallExpr).AdjustRet = true - yyVAL.expr = yyS[yypt-1].expr + yyDollar[2].expr.(*ast.FuncCallExpr).AdjustRet = true + yyVAL.expr = yyDollar[2].expr } case 73: - //line parser.go.y:410 + yyDollar = yyS[yypt-2 : yypt+1] +//line parser.go.y:413 { - yyVAL.expr = &ast.FuncCallExpr{Func: yyS[yypt-1].expr, Args: yyS[yypt-0].exprlist} - yyVAL.expr.SetLine(yyS[yypt-1].expr.Line()) + yyVAL.expr = &ast.FuncCallExpr{Func: yyDollar[1].expr, Args: yyDollar[2].exprlist} + yyVAL.expr.SetLine(yyDollar[1].expr.Line()) } case 74: - //line parser.go.y:414 + yyDollar = yyS[yypt-4 : yypt+1] +//line parser.go.y:417 { - yyVAL.expr = &ast.FuncCallExpr{Method: yyS[yypt-1].token.Str, Receiver: yyS[yypt-3].expr, Args: yyS[yypt-0].exprlist} - yyVAL.expr.SetLine(yyS[yypt-3].expr.Line()) + yyVAL.expr = &ast.FuncCallExpr{Method: yyDollar[3].token.Str, Receiver: yyDollar[1].expr, Args: yyDollar[4].exprlist} + yyVAL.expr.SetLine(yyDollar[1].expr.Line()) } case 75: - //line parser.go.y:420 + yyDollar = yyS[yypt-2 : yypt+1] +//line parser.go.y:423 { if yylex.(*Lexer).PNewLine { - yylex.(*Lexer).TokenError(yyS[yypt-1].token, "ambiguous syntax (function call x new statement)") + yylex.(*Lexer).TokenError(yyDollar[1].token, "ambiguous syntax (function call x new statement)") } yyVAL.exprlist = []ast.Expr{} } case 76: - //line parser.go.y:426 + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:429 { if yylex.(*Lexer).PNewLine { - yylex.(*Lexer).TokenError(yyS[yypt-2].token, "ambiguous syntax (function call x new statement)") + yylex.(*Lexer).TokenError(yyDollar[1].token, "ambiguous syntax (function call x new statement)") } - yyVAL.exprlist = yyS[yypt-1].exprlist + yyVAL.exprlist = yyDollar[2].exprlist } case 77: - //line parser.go.y:432 + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:435 { - yyVAL.exprlist = []ast.Expr{yyS[yypt-0].expr} + yyVAL.exprlist = []ast.Expr{yyDollar[1].expr} } case 78: - //line parser.go.y:435 + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:438 { - yyVAL.exprlist = []ast.Expr{yyS[yypt-0].expr} + yyVAL.exprlist = []ast.Expr{yyDollar[1].expr} } case 79: - //line parser.go.y:440 + yyDollar = yyS[yypt-2 : yypt+1] +//line parser.go.y:443 { - yyVAL.expr = &ast.FunctionExpr{ParList: yyS[yypt-0].funcexpr.ParList, Stmts: yyS[yypt-0].funcexpr.Stmts} - yyVAL.expr.SetLine(yyS[yypt-1].token.Pos.Line) - yyVAL.expr.SetLastLine(yyS[yypt-0].funcexpr.LastLine()) + yyVAL.expr = &ast.FunctionExpr{ParList: yyDollar[2].funcexpr.ParList, Stmts: yyDollar[2].funcexpr.Stmts} + yyVAL.expr.SetLine(yyDollar[1].token.Pos.Line) + yyVAL.expr.SetLastLine(yyDollar[2].funcexpr.LastLine()) } case 80: - //line parser.go.y:447 + yyDollar = yyS[yypt-5 : yypt+1] +//line parser.go.y:450 { - yyVAL.funcexpr = &ast.FunctionExpr{ParList: yyS[yypt-3].parlist, Stmts: yyS[yypt-1].stmts} - yyVAL.funcexpr.SetLine(yyS[yypt-4].token.Pos.Line) - yyVAL.funcexpr.SetLastLine(yyS[yypt-0].token.Pos.Line) + yyVAL.funcexpr = &ast.FunctionExpr{ParList: yyDollar[2].parlist, Stmts: yyDollar[4].stmts} + yyVAL.funcexpr.SetLine(yyDollar[1].token.Pos.Line) + yyVAL.funcexpr.SetLastLine(yyDollar[5].token.Pos.Line) } case 81: - //line parser.go.y:452 + yyDollar = yyS[yypt-4 : yypt+1] +//line parser.go.y:455 { - yyVAL.funcexpr = &ast.FunctionExpr{ParList: &ast.ParList{HasVargs: false, Names: []string{}}, Stmts: yyS[yypt-1].stmts} - yyVAL.funcexpr.SetLine(yyS[yypt-3].token.Pos.Line) - yyVAL.funcexpr.SetLastLine(yyS[yypt-0].token.Pos.Line) + yyVAL.funcexpr = &ast.FunctionExpr{ParList: &ast.ParList{HasVargs: false, Names: []string{}}, Stmts: yyDollar[3].stmts} + yyVAL.funcexpr.SetLine(yyDollar[1].token.Pos.Line) + yyVAL.funcexpr.SetLastLine(yyDollar[4].token.Pos.Line) } case 82: - //line parser.go.y:459 + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:462 { yyVAL.parlist = &ast.ParList{HasVargs: true, Names: []string{}} } case 83: - //line parser.go.y:462 + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:465 { yyVAL.parlist = &ast.ParList{HasVargs: false, Names: []string{}} - yyVAL.parlist.Names = append(yyVAL.parlist.Names, yyS[yypt-0].namelist...) + yyVAL.parlist.Names = append(yyVAL.parlist.Names, yyDollar[1].namelist...) } case 84: - //line parser.go.y:466 + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:469 { yyVAL.parlist = &ast.ParList{HasVargs: true, Names: []string{}} - yyVAL.parlist.Names = append(yyVAL.parlist.Names, yyS[yypt-2].namelist...) + yyVAL.parlist.Names = append(yyVAL.parlist.Names, yyDollar[1].namelist...) } case 85: - //line parser.go.y:473 + yyDollar = yyS[yypt-2 : yypt+1] +//line parser.go.y:476 { yyVAL.expr = &ast.TableExpr{Fields: []*ast.Field{}} - yyVAL.expr.SetLine(yyS[yypt-1].token.Pos.Line) + yyVAL.expr.SetLine(yyDollar[1].token.Pos.Line) } case 86: - //line parser.go.y:477 + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:480 { - yyVAL.expr = &ast.TableExpr{Fields: yyS[yypt-1].fieldlist} - yyVAL.expr.SetLine(yyS[yypt-2].token.Pos.Line) + yyVAL.expr = &ast.TableExpr{Fields: yyDollar[2].fieldlist} + yyVAL.expr.SetLine(yyDollar[1].token.Pos.Line) } case 87: - //line parser.go.y:484 + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:487 { - yyVAL.fieldlist = []*ast.Field{yyS[yypt-0].field} + yyVAL.fieldlist = []*ast.Field{yyDollar[1].field} } case 88: - //line parser.go.y:487 + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:490 { - yyVAL.fieldlist = append(yyS[yypt-2].fieldlist, yyS[yypt-0].field) + yyVAL.fieldlist = append(yyDollar[1].fieldlist, yyDollar[3].field) } case 89: - //line parser.go.y:490 + yyDollar = yyS[yypt-2 : yypt+1] +//line parser.go.y:493 { - yyVAL.fieldlist = yyS[yypt-1].fieldlist + yyVAL.fieldlist = yyDollar[1].fieldlist } case 90: - //line parser.go.y:495 + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:498 { - yyVAL.field = &ast.Field{Key: &ast.StringExpr{Value: yyS[yypt-2].token.Str}, Value: yyS[yypt-0].expr} - yyVAL.field.Key.SetLine(yyS[yypt-2].token.Pos.Line) + yyVAL.field = &ast.Field{Key: &ast.StringExpr{Value: yyDollar[1].token.Str}, Value: yyDollar[3].expr} + yyVAL.field.Key.SetLine(yyDollar[1].token.Pos.Line) } case 91: - //line parser.go.y:499 + yyDollar = yyS[yypt-5 : yypt+1] +//line parser.go.y:502 { - yyVAL.field = &ast.Field{Key: yyS[yypt-3].expr, Value: yyS[yypt-0].expr} + yyVAL.field = &ast.Field{Key: yyDollar[2].expr, Value: yyDollar[5].expr} } case 92: - //line parser.go.y:502 + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:505 { - yyVAL.field = &ast.Field{Value: yyS[yypt-0].expr} + yyVAL.field = &ast.Field{Value: yyDollar[1].expr} } case 93: - //line parser.go.y:507 + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:510 { yyVAL.fieldsep = "," } case 94: - //line parser.go.y:510 + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:513 { yyVAL.fieldsep = ";" } diff --git a/vendor/github.com/yuin/gopher-lua/parse/parser.go.y b/vendor/github.com/yuin/gopher-lua/parse/parser.go.y index 956133db29..9a9f831e61 100644 --- a/vendor/github.com/yuin/gopher-lua/parse/parser.go.y +++ b/vendor/github.com/yuin/gopher-lua/parse/parser.go.y @@ -396,6 +396,9 @@ prefixexp: $$ = $1 } | '(' expr ')' { + if ex, ok := $2.(*ast.Comma3Expr); ok { + ex.AdjustRet = true + } $$ = $2 $$.SetLine($1.Pos.Line) } diff --git a/vendor/github.com/yuin/gopher-lua/pm/pm.go b/vendor/github.com/yuin/gopher-lua/pm/pm.go index e15bc21005..e5c651f942 100644 --- a/vendor/github.com/yuin/gopher-lua/pm/pm.go +++ b/vendor/github.com/yuin/gopher-lua/pm/pm.go @@ -210,7 +210,7 @@ func (pn *singleClass) Matches(ch int) bool { case 'l', 'L': ret = 'a' <= ch && ch <= 'z' case 'p', 'P': - ret = (0x21 <= ch && ch <= 0x2f) || (0x30 <= ch && ch <= 0x40) || (0x5b <= ch && ch <= 0x60) || (0x7b <= ch && ch <= 0x7e) + ret = (0x21 <= ch && ch <= 0x2f) || (0x3a <= ch && ch <= 0x40) || (0x5b <= ch && ch <= 0x60) || (0x7b <= ch && ch <= 0x7e) case 's', 'S': switch ch { case ' ', '\f', '\n', '\r', '\t', '\v': diff --git a/vendor/github.com/yuin/gopher-lua/table.go b/vendor/github.com/yuin/gopher-lua/table.go index e220bd9c3b..ddf14dd88f 100644 --- a/vendor/github.com/yuin/gopher-lua/table.go +++ b/vendor/github.com/yuin/gopher-lua/table.go @@ -46,7 +46,7 @@ func newLTable(acap int, hcap int) *LTable { return tb } -// Len returns length of this LTable. +// Len returns length of this LTable without using __len. func (tb *LTable) Len() int { if tb.array == nil { return 0 diff --git a/vendor/github.com/yuin/gopher-lua/vm.go b/vendor/github.com/yuin/gopher-lua/vm.go index f3733f1300..aaa04dc9aa 100644 --- a/vendor/github.com/yuin/gopher-lua/vm.go +++ b/vendor/github.com/yuin/gopher-lua/vm.go @@ -549,7 +549,7 @@ func init() { if ret.Type() == LTNumber { reg.SetNumber(RA, ret.(LNumber)) } else { - reg.SetNumber(RA, LNumber(0)) + reg.Set(RA, ret) } } else if lv.Type() == LTTable { reg.SetNumber(RA, LNumber(lv.(*LTable).Len())) diff --git a/vendor/modules.txt b/vendor/modules.txt index 0cddf14b59..83bc03cd7d 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -87,7 +87,7 @@ github.com/alecthomas/units # github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a ## explicit github.com/alicebob/gopher-json -# github.com/alicebob/miniredis/v2 v2.22.0 +# github.com/alicebob/miniredis/v2 v2.23.1 ## explicit; go 1.14 github.com/alicebob/miniredis/v2 github.com/alicebob/miniredis/v2/geohash @@ -867,8 +867,8 @@ github.com/weaveworks/common/user # github.com/weaveworks/promrus v1.2.0 ## explicit github.com/weaveworks/promrus -# github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 -## explicit; go 1.14 +# github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64 +## explicit; go 1.17 github.com/yuin/gopher-lua github.com/yuin/gopher-lua/ast github.com/yuin/gopher-lua/parse