From aaae3e645618a373f828ee321abe0070dfd60a35 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 1 Nov 2024 14:46:11 +0000 Subject: [PATCH] fix(deps): update module github.com/alicebob/miniredis/v2 to v2.33.0 --- go.mod | 4 +- go.sum | 8 +- .../alicebob/miniredis/v2/CHANGELOG.md | 42 +- .../github.com/alicebob/miniredis/v2/Makefile | 37 +- .../alicebob/miniredis/v2/README.md | 12 +- .../alicebob/miniredis/v2/cmd_client.go | 68 ++ .../alicebob/miniredis/v2/cmd_generic.go | 330 ++++--- .../alicebob/miniredis/v2/cmd_geo.go | 2 +- .../alicebob/miniredis/v2/cmd_hash.go | 96 +- .../alicebob/miniredis/v2/cmd_list.go | 8 +- .../alicebob/miniredis/v2/cmd_object.go | 58 ++ .../alicebob/miniredis/v2/cmd_server.go | 65 ++ .../alicebob/miniredis/v2/cmd_set.go | 121 +++ .../alicebob/miniredis/v2/cmd_sorted_set.go | 340 ++++--- .../alicebob/miniredis/v2/cmd_stream.go | 48 +- vendor/github.com/alicebob/miniredis/v2/db.go | 106 ++- .../alicebob/miniredis/v2/direct.go | 7 +- .../alicebob/miniredis/v2/fpconv/LICENSE.txt | 26 + .../alicebob/miniredis/v2/fpconv/Makefile | 6 + .../alicebob/miniredis/v2/fpconv/README.md | 3 + .../alicebob/miniredis/v2/fpconv/dtoa.go | 286 ++++++ .../alicebob/miniredis/v2/fpconv/fp.go | 96 ++ .../alicebob/miniredis/v2/fpconv/powers.go | 82 ++ .../github.com/alicebob/miniredis/v2/lua.go | 9 + .../alicebob/miniredis/v2/miniredis.go | 33 +- .../github.com/alicebob/miniredis/v2/opts.go | 20 +- .../alicebob/miniredis/v2/proto/Makefile | 2 + .../alicebob/miniredis/v2/proto/client.go | 60 ++ .../alicebob/miniredis/v2/proto/proto.go | 288 ++++++ .../alicebob/miniredis/v2/proto/types.go | 102 +++ .../github.com/alicebob/miniredis/v2/redis.go | 11 +- .../alicebob/miniredis/v2/server/server.go | 31 +- .../alicebob/miniredis/v2/size/readme.md | 2 + .../alicebob/miniredis/v2/size/size.go | 138 +++ .../alicebob/miniredis/v2/stream.go | 18 +- vendor/github.com/yuin/gopher-lua/.gitignore | 1 + vendor/github.com/yuin/gopher-lua/_state.go | 54 +- vendor/github.com/yuin/gopher-lua/_vm.go | 130 +-- vendor/github.com/yuin/gopher-lua/auxlib.go | 7 +- vendor/github.com/yuin/gopher-lua/debuglib.go | 4 +- vendor/github.com/yuin/gopher-lua/iolib.go | 5 +- vendor/github.com/yuin/gopher-lua/oslib.go | 8 +- vendor/github.com/yuin/gopher-lua/state.go | 108 ++- vendor/github.com/yuin/gopher-lua/value.go | 66 +- vendor/github.com/yuin/gopher-lua/vm.go | 853 ++++++++++++++++-- vendor/modules.txt | 9 +- 46 files changed, 3250 insertions(+), 560 deletions(-) create mode 100644 vendor/github.com/alicebob/miniredis/v2/cmd_client.go create mode 100644 vendor/github.com/alicebob/miniredis/v2/cmd_object.go create mode 100644 vendor/github.com/alicebob/miniredis/v2/fpconv/LICENSE.txt create mode 100644 vendor/github.com/alicebob/miniredis/v2/fpconv/Makefile create mode 100644 vendor/github.com/alicebob/miniredis/v2/fpconv/README.md create mode 100644 vendor/github.com/alicebob/miniredis/v2/fpconv/dtoa.go create mode 100644 vendor/github.com/alicebob/miniredis/v2/fpconv/fp.go create mode 100644 vendor/github.com/alicebob/miniredis/v2/fpconv/powers.go create mode 100644 vendor/github.com/alicebob/miniredis/v2/proto/Makefile create mode 100644 vendor/github.com/alicebob/miniredis/v2/proto/client.go create mode 100644 vendor/github.com/alicebob/miniredis/v2/proto/proto.go create mode 100644 vendor/github.com/alicebob/miniredis/v2/proto/types.go create mode 100644 vendor/github.com/alicebob/miniredis/v2/size/readme.md create mode 100644 vendor/github.com/alicebob/miniredis/v2/size/size.go create mode 100644 vendor/github.com/yuin/gopher-lua/.gitignore diff --git a/go.mod b/go.mod index 37e9113ef42c6..7f8c34c8d89f3 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/Masterminds/sprig/v3 v3.2.3 github.com/NYTimes/gziphandler v1.1.1 github.com/Workiva/go-datastructures v1.1.5 - github.com/alicebob/miniredis/v2 v2.30.4 + github.com/alicebob/miniredis/v2 v2.33.0 github.com/aliyun/aliyun-oss-go-sdk v2.2.10+incompatible github.com/aws/aws-sdk-go v1.55.5 github.com/baidubce/bce-sdk-go v0.9.197 @@ -350,7 +350,7 @@ require ( github.com/willf/bitset v1.1.11 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect - github.com/yuin/gopher-lua v1.1.0 // indirect + github.com/yuin/gopher-lua v1.1.1 // indirect go.etcd.io/etcd/api/v3 v3.5.4 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.4 // indirect go.etcd.io/etcd/client/v3 v3.5.4 // indirect diff --git a/go.sum b/go.sum index 459090b348a3b..daff6aabf2179 100644 --- a/go.sum +++ b/go.sum @@ -322,8 +322,8 @@ github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30 h1:t3eaIm0rUkzbrI github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs= 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.30.4 h1:8S4/o1/KoUArAGbGwPxcwf0krlzceva2XVOSchFS7Eo= -github.com/alicebob/miniredis/v2 v2.30.4/go.mod h1:b25qWj4fCEsBeAAR2mlb0ufImGC6uH3VlUfb/HS5zKg= +github.com/alicebob/miniredis/v2 v2.33.0 h1:uvTF0EDeu9RLnUEG27Db5I68ESoIxTiXbNUiji6lZrA= +github.com/alicebob/miniredis/v2 v2.33.0/go.mod h1:MhP4a3EU7aENRi9aO+tHfTBZicLqQevyi/DJpoj6mi0= github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190808125512-07798873deee/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ= github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= github.com/aliyun/aliyun-oss-go-sdk v2.2.10+incompatible h1:ROMcuN61gI8SfQ+AEMh4d7GZ3gwTZLIhPjtd05TQCG4= @@ -1894,8 +1894,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/gopher-lua v0.0.0-20180630135845-46796da1b0b4/go.mod h1:aEV29XrmTYFr3CiRxZeGHpkvbwq+prZduBqMaascyCU= -github.com/yuin/gopher-lua v1.1.0 h1:BojcDhfyDWgU2f2TOzYK/g5p2gxMrku8oupLDqlnSqE= -github.com/yuin/gopher-lua v1.1.0/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= +github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M= +github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= diff --git a/vendor/github.com/alicebob/miniredis/v2/CHANGELOG.md b/vendor/github.com/alicebob/miniredis/v2/CHANGELOG.md index ba690a2d17e7d..28ed159c3140f 100644 --- a/vendor/github.com/alicebob/miniredis/v2/CHANGELOG.md +++ b/vendor/github.com/alicebob/miniredis/v2/CHANGELOG.md @@ -1,6 +1,46 @@ ## Changelog +### v2.33.0 + +- minimum Go version is now 1.17 +- fix integer overflow (thanks @wszaranski) +- test against the last BSD redis (7.2.4) +- ignore 'redis.set_repl()' call (thanks @TingluoHuang) +- various build fixes (thanks @wszaranski) +- add StartAddrTLS function (thanks @agriffaut) +- support for the NOMKSTREAM option for XADD (thanks @Jahaja) +- return empty array for SRANDMEMBER on nonexistent key (thanks @WKBae) + + +### v2.32.1 + +- support for SINTERCARD (thanks @s-barr-fetch) +- support for EXPIRETIME and PEXPIRETIME (thanks @wszaranski) +- fix GEO* units to be case insensitive + + +### v2.31.1 + +- support COUNT in SCAN and ZSCAN (thanks @BarakSilverfort) +- support for OBJECT IDLETIME (thanks @nerd2) +- support for HRANDFIELD (thanks @sejin-P) + + +### v2.31.0 + +- support for MEMORY USAGE (thanks @davidroman0O) +- test against Redis 7.2.0 +- support for CLIENT SETNAME/GETNAME (thanks @mr-karan) +- fix very small numbers (thanks @zsh1995) +- use the same float-to-string logic real Redis uses + + +### v2.30.5 + +- support SMISMEMBER (thanks @sandyharvie) + + ### v2.30.4 - fix ZADD LT/LG (thanks @sejin-P) @@ -97,7 +137,7 @@ ### v2.16.1 -- fix ZINTERSTORE with wets (thanks @lingjl2010 and @okhowang) +- fix ZINTERSTORE with sets (thanks @lingjl2010 and @okhowang) - fix exclusive ranges in XRANGE (thanks @joseotoro) diff --git a/vendor/github.com/alicebob/miniredis/v2/Makefile b/vendor/github.com/alicebob/miniredis/v2/Makefile index 125f574cffe47..2b5ec3eca9acb 100644 --- a/vendor/github.com/alicebob/miniredis/v2/Makefile +++ b/vendor/github.com/alicebob/miniredis/v2/Makefile @@ -1,12 +1,33 @@ -.PHONY: all test testrace int - -all: test - -test: +.PHONY: test +test: ### Run unit tests go test ./... -testrace: +.PHONY: testrace +testrace: ### Run unit tests with race detector go test -race ./... -int: - INT=1 go test ./... +.PHONY: int +int: ### Run integration tests (doesn't download redis server) + ${MAKE} -C integration int + +.PHONY: ci +ci: ### Run full tests suite (including download and compilation of proper redis server) + ${MAKE} test + ${MAKE} -C integration redis_src/redis-server int + ${MAKE} testrace + +.PHONY: clean +clean: ### Clean integration test files and remove compiled redis from integration/redis_src + ${MAKE} -C integration clean + +.PHONY: help +help: +ifeq ($(UNAME), Linux) + @grep -P '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | \ + awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' +else + @# this is not tested, but prepared in advance for you, Mac drivers + @awk -F ':.*###' '$$0 ~ FS {printf "%15s%s\n", $$1 ":", $$2}' \ + $(MAKEFILE_LIST) | grep -v '@awk' | sort +endif + diff --git a/vendor/github.com/alicebob/miniredis/v2/README.md b/vendor/github.com/alicebob/miniredis/v2/README.md index 3ec7ea28429c5..5d4e4d8ba76a5 100644 --- a/vendor/github.com/alicebob/miniredis/v2/README.md +++ b/vendor/github.com/alicebob/miniredis/v2/README.md @@ -39,15 +39,17 @@ Implemented commands: - EXISTS - EXPIRE - EXPIREAT + - EXPIRETIME - KEYS - MOVE - PERSIST - PEXPIRE - PEXPIREAT + - PEXPIRETIME - PTTL + - RANDOMKEY -- see m.Seed(...) - RENAME - RENAMENX - - RANDOMKEY -- see m.Seed(...) - SCAN - TOUCH - TTL @@ -103,6 +105,7 @@ Implemented commands: - HLEN - HMGET - HMSET + - HRANDFIELD - HSET - HSETNX - HSTRLEN @@ -142,20 +145,23 @@ Implemented commands: - SDIFFSTORE - SINTER - SINTERSTORE + - SINTERCARD - SISMEMBER - SMEMBERS + - SMISMEMBER - SMOVE - SPOP -- see m.Seed(...) - SRANDMEMBER -- see m.Seed(...) - SREM + - SSCAN - SUNION - SUNIONSTORE - - SSCAN - Sorted Set keys (complete) - ZADD - ZCARD - ZCOUNT - ZINCRBY + - ZINTER - ZINTERSTORE - ZLEXCOUNT - ZPOPMIN @@ -321,7 +327,7 @@ Commands which will probably not be implemented: ## &c. -Integration tests are run against Redis 7.0.7. The [./integration](./integration/) subdir +Integration tests are run against Redis 7.2.4. The [./integration](./integration/) subdir compares miniredis against a real redis instance. The Redis 6 RESP3 protocol is supported. If there are problems, please open diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_client.go b/vendor/github.com/alicebob/miniredis/v2/cmd_client.go new file mode 100644 index 0000000000000..ca9fcd9a4d4e8 --- /dev/null +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_client.go @@ -0,0 +1,68 @@ +package miniredis + +import ( + "fmt" + "strings" + + "github.com/alicebob/miniredis/v2/server" +) + +// commandsClient handles client operations. +func commandsClient(m *Miniredis) { + m.srv.Register("CLIENT", m.cmdClient) +} + +// CLIENT +func (m *Miniredis) cmdClient(c *server.Peer, cmd string, args []string) { + if len(args) == 0 { + setDirty(c) + c.WriteError("ERR wrong number of arguments for 'client' command") + return + } + + withTx(m, c, func(c *server.Peer, ctx *connCtx) { + switch cmd := strings.ToUpper(args[0]); cmd { + case "SETNAME": + m.cmdClientSetName(c, args[1:]) + case "GETNAME": + m.cmdClientGetName(c, args[1:]) + default: + setDirty(c) + c.WriteError(fmt.Sprintf("ERR unknown subcommand '%s'. Try CLIENT HELP.", cmd)) + } + }) +} + +// CLIENT SETNAME +func (m *Miniredis) cmdClientSetName(c *server.Peer, args []string) { + if len(args) != 1 { + setDirty(c) + c.WriteError("ERR wrong number of arguments for 'client setname' command") + return + } + + name := args[0] + if strings.ContainsAny(name, " \n") { + setDirty(c) + c.WriteError("ERR Client names cannot contain spaces, newlines or special characters.") + return + + } + c.ClientName = name + c.WriteOK() +} + +// CLIENT GETNAME +func (m *Miniredis) cmdClientGetName(c *server.Peer, args []string) { + if len(args) > 0 { + setDirty(c) + c.WriteError("ERR wrong number of arguments for 'client getname' command") + return + } + + if c.ClientName == "" { + c.WriteNull() + } else { + c.WriteBulk(c.ClientName) + } +} diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_generic.go b/vendor/github.com/alicebob/miniredis/v2/cmd_generic.go index 55e208971408c..721ad2fabe919 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_generic.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_generic.go @@ -3,6 +3,7 @@ package miniredis import ( + "errors" "fmt" "sort" "strconv" @@ -12,6 +13,21 @@ import ( "github.com/alicebob/miniredis/v2/server" ) +const ( + // expiretimeReplyNoExpiration is return value for EXPIRETIME and PEXPIRETIME if the key exists but has no associated expiration time + expiretimeReplyNoExpiration = -1 + // expiretimeReplyMissingKey is return value for EXPIRETIME and PEXPIRETIME if the key does not exist + expiretimeReplyMissingKey = -2 +) + +func inSeconds(t time.Time) int { + return int(t.Unix()) +} + +func inMilliSeconds(t time.Time) int { + return int(t.UnixMilli()) +} + // commandsGeneric handles EXPIRE, TTL, PERSIST, &c. func commandsGeneric(m *Miniredis) { m.srv.Register("COPY", m.cmdCopy) @@ -20,6 +36,8 @@ func commandsGeneric(m *Miniredis) { m.srv.Register("EXISTS", m.cmdExists) m.srv.Register("EXPIRE", makeCmdExpire(m, false, time.Second)) m.srv.Register("EXPIREAT", makeCmdExpire(m, true, time.Second)) + m.srv.Register("EXPIRETIME", m.makeCmdExpireTime(inSeconds)) + m.srv.Register("PEXPIRETIME", m.makeCmdExpireTime(inMilliSeconds)) m.srv.Register("KEYS", m.cmdKeys) // MIGRATE m.srv.Register("MOVE", m.cmdMove) @@ -40,6 +58,47 @@ func commandsGeneric(m *Miniredis) { m.srv.Register("UNLINK", m.cmdDel) } +type expireOpts struct { + key string + value int + nx bool + xx bool + gt bool + lt bool +} + +func expireParse(cmd string, args []string) (*expireOpts, error) { + var opts expireOpts + + opts.key = args[0] + if err := optIntSimple(args[1], &opts.value); err != nil { + return nil, err + } + args = args[2:] + for len(args) > 0 { + switch strings.ToLower(args[0]) { + case "nx": + opts.nx = true + case "xx": + opts.xx = true + case "gt": + opts.gt = true + case "lt": + opts.lt = true + default: + return nil, fmt.Errorf("ERR Unsupported option %s", args[0]) + } + args = args[1:] + } + if opts.gt && opts.lt { + return nil, errors.New("ERR GT and LT options at the same time are not compatible") + } + if opts.nx && (opts.xx || opts.gt || opts.lt) { + return nil, errors.New("ERR NX and XX, GT or LT options at the same time are not compatible") + } + return &opts, nil +} + // generic expire command for EXPIRE, PEXPIRE, EXPIREAT, PEXPIREAT // d is the time unit. If unix is set it'll be seen as a unixtimestamp and // converted to a duration. @@ -57,44 +116,10 @@ func makeCmdExpire(m *Miniredis, unix bool, d time.Duration) func(*server.Peer, return } - var opts struct { - key string - value int - nx bool - xx bool - gt bool - lt bool - } - opts.key = args[0] - if ok := optInt(c, args[1], &opts.value); !ok { - return - } - args = args[2:] - for len(args) > 0 { - switch strings.ToLower(args[0]) { - case "nx": - opts.nx = true - case "xx": - opts.xx = true - case "gt": - opts.gt = true - case "lt": - opts.lt = true - default: - setDirty(c) - c.WriteError(fmt.Sprintf("ERR Unsupported option %s", args[0])) - return - } - args = args[1:] - } - if opts.gt && opts.lt { + opts, err := expireParse(cmd, args) + if err != nil { setDirty(c) - c.WriteError("ERR GT and LT options at the same time are not compatible") - return - } - if opts.nx && (opts.xx || opts.gt || opts.lt) { - setDirty(c) - c.WriteError("ERR NX and XX, GT or LT options at the same time are not compatible") + c.WriteError(err.Error()) return } @@ -138,13 +163,54 @@ func makeCmdExpire(m *Miniredis, unix bool, d time.Duration) func(*server.Peer, return } db.ttl[opts.key] = newTTL - db.keyVersion[opts.key]++ + db.incr(opts.key) db.checkTTL(opts.key) c.WriteInt(1) }) } } +// makeCmdExpireTime creates server command function that returns the absolute Unix timestamp (since January 1, 1970) +// at which the given key will expire, in unit selected by time result strategy (e.g. seconds, milliseconds). +// For more information see redis documentation for [expiretime] and [pexpiretime]. +// +// [expiretime]: https://redis.io/commands/expiretime/ +// [pexpiretime]: https://redis.io/commands/pexpiretime/ +func (m *Miniredis) makeCmdExpireTime(timeResultStrategy func(time.Time) int) server.Cmd { + return func(c *server.Peer, cmd string, args []string) { + if len(args) != 1 { + setDirty(c) + c.WriteError(errWrongNumber(cmd)) + return + } + + if !m.handleAuth(c) { + return + } + if m.checkPubsub(c, cmd) { + return + } + + key := args[0] + withTx(m, c, func(c *server.Peer, ctx *connCtx) { + db := m.db(ctx.selectedDB) + + if _, ok := db.keys[key]; !ok { + c.WriteInt(expiretimeReplyMissingKey) + return + } + + ttl, ok := db.ttl[key] + if !ok { + c.WriteInt(expiretimeReplyNoExpiration) + return + } + + c.WriteInt(timeResultStrategy(m.effectiveNow().Add(ttl))) + }) + } +} + // TOUCH func (m *Miniredis) cmdTouch(c *server.Peer, cmd string, args []string) { if !m.handleAuth(c) { @@ -274,7 +340,7 @@ func (m *Miniredis) cmdPersist(c *server.Peer, cmd string, args []string) { return } delete(db.ttl, key) - db.keyVersion[key]++ + db.incr(key) c.WriteInt(1) }) } @@ -536,55 +602,42 @@ func (m *Miniredis) cmdRenamenx(c *server.Peer, cmd string, args []string) { }) } -// SCAN -func (m *Miniredis) cmdScan(c *server.Peer, cmd string, args []string) { - if len(args) < 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { - return - } - - var opts struct { - cursor int - withMatch bool - match string - withType bool - _type string - } +type scanOpts struct { + cursor int + count int + withMatch bool + match string + withType bool + _type string +} - if ok := optIntErr(c, args[0], &opts.cursor, msgInvalidCursor); !ok { - return +func scanParse(cmd string, args []string) (*scanOpts, error) { + var opts scanOpts + if err := optIntSimple(args[0], &opts.cursor); err != nil { + return nil, errors.New(msgInvalidCursor) } args = args[1:] // MATCH, COUNT and TYPE options for len(args) > 0 { if strings.ToLower(args[0]) == "count" { - // we do nothing with count if len(args) < 2 { - setDirty(c) - c.WriteError(msgSyntaxError) - return + return nil, errors.New(msgSyntaxError) } - if _, err := strconv.Atoi(args[1]); err != nil { - setDirty(c) - c.WriteError(msgInvalidInt) - return + count, err := strconv.Atoi(args[1]) + if err != nil || count < 0 { + return nil, errors.New(msgInvalidInt) + } + if count == 0 { + return nil, errors.New(msgSyntaxError) } + opts.count = count args = args[2:] continue } if strings.ToLower(args[0]) == "match" { if len(args) < 2 { - setDirty(c) - c.WriteError(msgSyntaxError) - return + return nil, errors.New(msgSyntaxError) } opts.withMatch = true opts.match, args = args[1], args[2:] @@ -592,31 +645,41 @@ func (m *Miniredis) cmdScan(c *server.Peer, cmd string, args []string) { } if strings.ToLower(args[0]) == "type" { if len(args) < 2 { - setDirty(c) - c.WriteError(msgSyntaxError) - return + return nil, errors.New(msgSyntaxError) } opts.withType = true opts._type, args = strings.ToLower(args[1]), args[2:] continue } + return nil, errors.New(msgSyntaxError) + } + return &opts, nil +} + +// SCAN +func (m *Miniredis) cmdScan(c *server.Peer, cmd string, args []string) { + if len(args) < 1 { + setDirty(c) + c.WriteError(errWrongNumber(cmd)) + return + } + if !m.handleAuth(c) { + return + } + if m.checkPubsub(c, cmd) { + return + } + + opts, err := scanParse(cmd, args) + if err != nil { setDirty(c) - c.WriteError(msgSyntaxError) + c.WriteError(err.Error()) return } withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) // We return _all_ (matched) keys every time. - - if opts.cursor != 0 { - // Invalid cursor. - c.WriteLen(2) - c.WriteBulk("0") // no next cursor - c.WriteLen(0) // no elements - return - } - var keys []string if opts.withType { @@ -627,17 +690,37 @@ func (m *Miniredis) cmdScan(c *server.Peer, cmd string, args []string) { keys = append(keys, k) } } - sort.Strings(keys) // To make things deterministic. } else { keys = db.allKeys() } + sort.Strings(keys) // To make things deterministic. + if opts.withMatch { keys, _ = matchKeys(keys, opts.match) } + low := opts.cursor + high := low + opts.count + // validate high is correct + if high > len(keys) || high == 0 { + high = len(keys) + } + if opts.cursor > high { + // invalid cursor + c.WriteLen(2) + c.WriteBulk("0") // no next cursor + c.WriteLen(0) // no elements + return + } + cursorValue := low + opts.count + if cursorValue >= len(keys) { + cursorValue = 0 // no next cursor + } + keys = keys[low:high] + c.WriteLen(2) - c.WriteBulk("0") // no next cursor + c.WriteBulk(fmt.Sprintf("%d", cursorValue)) c.WriteLen(len(keys)) for _, k := range keys { c.WriteBulk(k) @@ -645,26 +728,15 @@ func (m *Miniredis) cmdScan(c *server.Peer, cmd string, args []string) { }) } -// COPY -func (m *Miniredis) cmdCopy(c *server.Peer, cmd string, args []string) { - if len(args) < 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { - return - } +type copyOpts struct { + from string + to string + destinationDB int + replace bool +} - var opts = struct { - from string - to string - destinationDB int - replace bool - }{ +func copyParse(cmd string, args []string) (*copyOpts, error) { + opts := copyOpts{ destinationDB: -1, } @@ -673,33 +745,45 @@ func (m *Miniredis) cmdCopy(c *server.Peer, cmd string, args []string) { switch strings.ToLower(args[0]) { case "db": if len(args) < 2 { - setDirty(c) - c.WriteError(msgSyntaxError) - return + return nil, errors.New(msgSyntaxError) } - db, err := strconv.Atoi(args[1]) - if err != nil { - setDirty(c) - c.WriteError(msgInvalidInt) - return + if err := optIntSimple(args[1], &opts.destinationDB); err != nil { + return nil, err } - if db < 0 { - setDirty(c) - c.WriteError(msgDBIndexOutOfRange) - return + if opts.destinationDB < 0 { + return nil, errors.New(msgDBIndexOutOfRange) } - opts.destinationDB = db args = args[2:] case "replace": opts.replace = true args = args[1:] default: - setDirty(c) - c.WriteError(msgSyntaxError) - return + return nil, errors.New(msgSyntaxError) } } + return &opts, nil +} + +// COPY +func (m *Miniredis) cmdCopy(c *server.Peer, cmd string, args []string) { + if len(args) < 2 { + setDirty(c) + c.WriteError(errWrongNumber(cmd)) + return + } + if !m.handleAuth(c) { + return + } + if m.checkPubsub(c, cmd) { + return + } + opts, err := copyParse(cmd, args) + if err != nil { + setDirty(c) + c.WriteError(err.Error()) + return + } withTx(m, c, func(c *server.Peer, ctx *connCtx) { fromDB, toDB := ctx.selectedDB, opts.destinationDB if toDB == -1 { diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_geo.go b/vendor/github.com/alicebob/miniredis/v2/cmd_geo.go index 29a92d550f8b8..6702e96ba0ae4 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_geo.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_geo.go @@ -594,7 +594,7 @@ func withinRadius(members []ssElem, longitude, latitude, radius float64) []geoDi } func parseUnit(u string) float64 { - switch u { + switch strings.ToLower(u) { case "m": return 1 case "km": diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_hash.go b/vendor/github.com/alicebob/miniredis/v2/cmd_hash.go index 09fa4522f3770..2c45630a1a460 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_hash.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_hash.go @@ -27,6 +27,7 @@ func commandsHash(m *Miniredis) { m.srv.Register("HSTRLEN", m.cmdHstrlen) m.srv.Register("HVALS", m.cmdHvals) m.srv.Register("HSCAN", m.cmdHscan) + m.srv.Register("HRANDFIELD", m.cmdHrandfield) } // HSET @@ -105,7 +106,7 @@ func (m *Miniredis) cmdHsetnx(c *server.Peer, cmd string, args []string) { return } db.hashKeys[opts.key][opts.field] = opts.value - db.keyVersion[opts.key]++ + db.incr(opts.key) c.WriteInt(1) }) } @@ -681,3 +682,96 @@ func (m *Miniredis) cmdHscan(c *server.Peer, cmd string, args []string) { } }) } + +// HRANDFIELD +func (m *Miniredis) cmdHrandfield(c *server.Peer, cmd string, args []string) { + if len(args) > 3 || len(args) < 1 { + setDirty(c) + c.WriteError(errWrongNumber(cmd)) + return + } + if !m.handleAuth(c) { + return + } + if m.checkPubsub(c, cmd) { + return + } + + opts := struct { + key string + count int + countSet bool + withValues bool + }{ + key: args[0], + } + + if len(args) > 1 { + if ok := optIntErr(c, args[1], &opts.count, msgInvalidInt); !ok { + return + } + opts.countSet = true + } + + if len(args) == 3 { + if strings.ToLower(args[2]) == "withvalues" { + opts.withValues = true + } else { + setDirty(c) + c.WriteError(msgSyntaxError) + return + } + } + + withTx(m, c, func(peer *server.Peer, ctx *connCtx) { + db := m.db(ctx.selectedDB) + members := db.hashFields(opts.key) + m.shuffle(members) + + if !opts.countSet { + // > When called with just the key argument, return a random field from the + // hash value stored at key. + if len(members) == 0 { + peer.WriteNull() + return + } + peer.WriteBulk(members[0]) + return + } + + if len(members) > abs(opts.count) { + members = members[:abs(opts.count)] + } + switch { + case opts.count >= 0: + // if count is positive there can't be duplicates, and the length is restricted + case opts.count < 0: + // if count is negative there can be duplicates, but length will match + if len(members) > 0 { + for len(members) < -opts.count { + members = append(members, members[m.randIntn(len(members))]) + } + } + } + + if opts.withValues { + peer.WriteMapLen(len(members)) + for _, m := range members { + peer.WriteBulk(m) + peer.WriteBulk(db.hashGet(opts.key, m)) + } + return + } + peer.WriteLen(len(members)) + for _, m := range members { + peer.WriteBulk(m) + } + }) +} + +func abs(n int) int { + if n < 0 { + return -n + } + return n +} diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_list.go b/vendor/github.com/alicebob/miniredis/v2/cmd_list.go index 3d1cd02eaa3d4..0683cccc9c909 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_list.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_list.go @@ -368,7 +368,7 @@ func (m *Miniredis) cmdLinsert(c *server.Peer, cmd string, args []string) { } } db.listKeys[key] = l - db.keyVersion[key]++ + db.incr(key) c.WriteInt(len(l)) return } @@ -715,7 +715,7 @@ func (m *Miniredis) cmdLrem(c *server.Peer, cmd string, args []string) { db.del(opts.key, true) } else { db.listKeys[opts.key] = newL - db.keyVersion[opts.key]++ + db.incr(opts.key) } c.WriteInt(deleted) @@ -769,7 +769,7 @@ func (m *Miniredis) cmdLset(c *server.Peer, cmd string, args []string) { return } l[index] = opts.value - db.keyVersion[opts.key]++ + db.incr(opts.key) c.WriteOK() }) @@ -823,7 +823,7 @@ func (m *Miniredis) cmdLtrim(c *server.Peer, cmd string, args []string) { db.del(opts.key, true) } else { db.listKeys[opts.key] = l - db.keyVersion[opts.key]++ + db.incr(opts.key) } c.WriteOK() }) diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_object.go b/vendor/github.com/alicebob/miniredis/v2/cmd_object.go new file mode 100644 index 0000000000000..b958a95cfe191 --- /dev/null +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_object.go @@ -0,0 +1,58 @@ +package miniredis + +import ( + "fmt" + "strings" + + "github.com/alicebob/miniredis/v2/server" +) + +// commandsObject handles all object operations. +func commandsObject(m *Miniredis) { + m.srv.Register("OBJECT", m.cmdObject) +} + +// OBJECT +func (m *Miniredis) cmdObject(c *server.Peer, cmd string, args []string) { + if len(args) == 0 { + setDirty(c) + c.WriteError(errWrongNumber(cmd)) + return + } + if !m.handleAuth(c) { + return + } + if m.checkPubsub(c, cmd) { + return + } + + switch sub := strings.ToLower(args[0]); sub { + case "idletime": + m.cmdObjectIdletime(c, args[1:]) + default: + setDirty(c) + c.WriteError(fmt.Sprintf(msgFObjectUsage, sub)) + } +} + +// OBJECT IDLETIME +func (m *Miniredis) cmdObjectIdletime(c *server.Peer, args []string) { + if len(args) != 1 { + setDirty(c) + c.WriteError(errWrongNumber("object|idletime")) + return + } + key := args[0] + + withTx(m, c, func(c *server.Peer, ctx *connCtx) { + db := m.db(ctx.selectedDB) + + t, ok := db.lru[key] + if !ok { + c.WriteNull() + return + } + + c.WriteInt(int(db.master.effectiveNow().Sub(t).Seconds())) + }) +} diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_server.go b/vendor/github.com/alicebob/miniredis/v2/cmd_server.go index 6e51727ba59e2..b1c81569157dd 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_server.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_server.go @@ -3,10 +3,12 @@ package miniredis import ( + "fmt" "strconv" "strings" "github.com/alicebob/miniredis/v2/server" + "github.com/alicebob/miniredis/v2/size" ) func commandsServer(m *Miniredis) { @@ -16,6 +18,69 @@ func commandsServer(m *Miniredis) { m.srv.Register("FLUSHDB", m.cmdFlushdb) m.srv.Register("INFO", m.cmdInfo) m.srv.Register("TIME", m.cmdTime) + m.srv.Register("MEMORY", m.cmdMemory) +} + +// MEMORY +func (m *Miniredis) cmdMemory(c *server.Peer, cmd string, args []string) { + if len(args) == 0 { + setDirty(c) + c.WriteError(errWrongNumber(cmd)) + return + } + if !m.handleAuth(c) { + return + } + if m.checkPubsub(c, cmd) { + return + } + + withTx(m, c, func(c *server.Peer, ctx *connCtx) { + db := m.db(ctx.selectedDB) + + cmd, args := args[0], args[1:] + switch cmd { + case "USAGE": + if len(args) < 1 { + setDirty(c) + c.WriteError(errWrongNumber("memory|usage")) + return + } + if len(args) > 1 { + setDirty(c) + c.WriteError(msgSyntaxError) + return + } + + var ( + value interface{} + ok bool + ) + switch db.keys[args[0]] { + case "string": + value, ok = db.stringKeys[args[0]] + case "set": + value, ok = db.setKeys[args[0]] + case "hash": + value, ok = db.hashKeys[args[0]] + case "list": + value, ok = db.listKeys[args[0]] + case "hll": + value, ok = db.hllKeys[args[0]] + case "zset": + value, ok = db.sortedsetKeys[args[0]] + case "stream": + value, ok = db.streamKeys[args[0]] + } + if !ok { + c.WriteNull() + return + } + c.WriteInt(size.Of(value)) + default: + c.WriteError(fmt.Sprintf(msgMemorySubcommand, strings.ToUpper(cmd))) + } + }) } // DBSIZE diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_set.go b/vendor/github.com/alicebob/miniredis/v2/cmd_set.go index 056a3093e2b45..abcd1baa111c9 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_set.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_set.go @@ -16,10 +16,12 @@ func commandsSet(m *Miniredis) { m.srv.Register("SCARD", m.cmdScard) m.srv.Register("SDIFF", m.cmdSdiff) m.srv.Register("SDIFFSTORE", m.cmdSdiffstore) + m.srv.Register("SINTERCARD", m.cmdSintercard) m.srv.Register("SINTER", m.cmdSinter) m.srv.Register("SINTERSTORE", m.cmdSinterstore) m.srv.Register("SISMEMBER", m.cmdSismember) m.srv.Register("SMEMBERS", m.cmdSmembers) + m.srv.Register("SMISMEMBER", m.cmdSmismember) m.srv.Register("SMOVE", m.cmdSmove) m.srv.Register("SPOP", m.cmdSpop) m.srv.Register("SRANDMEMBER", m.cmdSrandmember) @@ -218,6 +220,77 @@ func (m *Miniredis) cmdSinterstore(c *server.Peer, cmd string, args []string) { }) } +// SINTERCARD +func (m *Miniredis) cmdSintercard(c *server.Peer, cmd string, args []string) { + if len(args) < 2 { + setDirty(c) + c.WriteError(errWrongNumber(cmd)) + return + } + if !m.handleAuth(c) { + return + } + if m.checkPubsub(c, cmd) { + return + } + + opts := struct { + keys []string + limit int + }{} + + numKeys, err := strconv.Atoi(args[0]) + if err != nil { + setDirty(c) + c.WriteError("ERR numkeys should be greater than 0") + return + } + if numKeys < 1 { + setDirty(c) + c.WriteError("ERR numkeys should be greater than 0") + return + } + + args = args[1:] + if len(args) < numKeys { + setDirty(c) + c.WriteError("ERR Number of keys can't be greater than number of args") + return + } + opts.keys = args[:numKeys] + + args = args[numKeys:] + if len(args) == 2 && strings.ToLower(args[0]) == "limit" { + l, err := strconv.Atoi(args[1]) + if err != nil { + setDirty(c) + c.WriteError(msgInvalidInt) + return + } + if l < 0 { + setDirty(c) + c.WriteError(msgLimitIsNegative) + return + } + opts.limit = l + } else if len(args) > 0 { + setDirty(c) + c.WriteError(msgSyntaxError) + return + } + + withTx(m, c, func(c *server.Peer, ctx *connCtx) { + db := m.db(ctx.selectedDB) + + count, err := db.setIntercard(opts.keys, opts.limit) + if err != nil { + c.WriteError(err.Error()) + return + } + c.WriteInt(count) + }) +} + // SISMEMBER func (m *Miniredis) cmdSismember(c *server.Peer, cmd string, args []string) { if len(args) != 2 { @@ -293,6 +366,50 @@ func (m *Miniredis) cmdSmembers(c *server.Peer, cmd string, args []string) { }) } +// SMISMEMBER +func (m *Miniredis) cmdSmismember(c *server.Peer, cmd string, args []string) { + if len(args) < 2 { + setDirty(c) + c.WriteError(errWrongNumber(cmd)) + return + } + if !m.handleAuth(c) { + return + } + if m.checkPubsub(c, cmd) { + return + } + + key, values := args[0], args[1:] + + withTx(m, c, func(c *server.Peer, ctx *connCtx) { + db := m.db(ctx.selectedDB) + + if !db.exists(key) { + c.WriteLen(len(values)) + for range values { + c.WriteInt(0) + } + return + } + + if db.t(key) != "set" { + c.WriteError(ErrWrongType.Error()) + return + } + + c.WriteLen(len(values)) + for _, value := range values { + if db.setIsMember(key, value) { + c.WriteInt(1) + } else { + c.WriteInt(0) + } + } + return + }) +} + // SMOVE func (m *Miniredis) cmdSmove(c *server.Peer, cmd string, args []string) { if len(args) != 3 { @@ -465,6 +582,10 @@ func (m *Miniredis) cmdSrandmember(c *server.Peer, cmd string, args []string) { db := m.db(ctx.selectedDB) if !db.exists(key) { + if withCount { + c.WriteLen(0) + return + } c.WriteNull() return } 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 e7164e04851a4..ab943addc4246 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_sorted_set.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_sorted_set.go @@ -4,6 +4,7 @@ package miniredis import ( "errors" + "fmt" "math" "sort" "strconv" @@ -18,7 +19,8 @@ func commandsSortedSet(m *Miniredis) { m.srv.Register("ZCARD", m.cmdZcard) m.srv.Register("ZCOUNT", m.cmdZcount) m.srv.Register("ZINCRBY", m.cmdZincrby) - m.srv.Register("ZINTERSTORE", m.cmdZinterstore) + m.srv.Register("ZINTER", m.makeCmdZinter(false)) + m.srv.Register("ZINTERSTORE", m.makeCmdZinter(true)) m.srv.Register("ZLEXCOUNT", m.cmdZlexcount) m.srv.Register("ZRANGE", m.cmdZrange) m.srv.Register("ZRANGEBYLEX", m.makeCmdZrangebylex(false)) @@ -323,145 +325,192 @@ func (m *Miniredis) cmdZincrby(c *server.Peer, cmd string, args []string) { }) } -// ZINTERSTORE -func (m *Miniredis) cmdZinterstore(c *server.Peer, cmd string, args []string) { - if len(args) < 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { - return - } +// ZINTERSTORE and ZINTER +func (m *Miniredis) makeCmdZinter(store bool) func(c *server.Peer, cmd string, args []string) { + return func(c *server.Peer, cmd string, args []string) { + minArgs := 2 + if store { + minArgs++ + } + if len(args) < minArgs { + setDirty(c) + c.WriteError(errWrongNumber(cmd)) + return + } + if !m.handleAuth(c) { + return + } + if m.checkPubsub(c, cmd) { + return + } - destination := args[0] - numKeys, err := strconv.Atoi(args[1]) - if err != nil { - setDirty(c) - c.WriteError(msgInvalidInt) - return - } - args = args[2:] - if len(args) < numKeys { - setDirty(c) - c.WriteError(msgSyntaxError) - return - } - if numKeys <= 0 { - setDirty(c) - c.WriteError("ERR at least 1 input key is needed for ZUNIONSTORE/ZINTERSTORE") - return - } - keys := args[:numKeys] - args = args[numKeys:] + var opts = struct { + Store bool // if true this is ZINTERSTORE + Destination string // only relevant if $store is true + Keys []string + Aggregate string + WithWeights bool + Weights []float64 + WithScores bool // only for ZINTER + }{ + Store: store, + Aggregate: "sum", + } - withWeights := false - weights := []float64{} - aggregate := "sum" - for len(args) > 0 { - switch strings.ToLower(args[0]) { - case "weights": - if len(args) < numKeys+1 { - setDirty(c) - c.WriteError(msgSyntaxError) - return - } - for i := 0; i < numKeys; i++ { - f, err := strconv.ParseFloat(args[i+1], 64) - if err != nil { + if store { + opts.Destination = args[0] + args = args[1:] + } + numKeys, err := strconv.Atoi(args[0]) + if err != nil { + setDirty(c) + c.WriteError(msgInvalidInt) + return + } + args = args[1:] + if len(args) < numKeys { + setDirty(c) + c.WriteError(msgSyntaxError) + return + } + if numKeys <= 0 { + setDirty(c) + c.WriteError("ERR at least 1 input key is needed for ZUNIONSTORE/ZINTERSTORE") + return + } + opts.Keys = args[:numKeys] + args = args[numKeys:] + + for len(args) > 0 { + switch strings.ToLower(args[0]) { + case "weights": + if len(args) < numKeys+1 { setDirty(c) - c.WriteError("ERR weight value is not a float") + c.WriteError(msgSyntaxError) return } - weights = append(weights, f) - } - withWeights = true - args = args[numKeys+1:] - case "aggregate": - if len(args) < 2 { - setDirty(c) - c.WriteError(msgSyntaxError) - return - } - aggregate = strings.ToLower(args[1]) - switch aggregate { - case "sum", "min", "max": + for i := 0; i < numKeys; i++ { + f, err := strconv.ParseFloat(args[i+1], 64) + if err != nil { + setDirty(c) + c.WriteError("ERR weight value is not a float") + return + } + opts.Weights = append(opts.Weights, f) + } + opts.WithWeights = true + args = args[numKeys+1:] + case "aggregate": + if len(args) < 2 { + setDirty(c) + c.WriteError(msgSyntaxError) + return + } + aggregate := strings.ToLower(args[1]) + switch aggregate { + case "sum", "min", "max": + opts.Aggregate = aggregate + default: + setDirty(c) + c.WriteError(msgSyntaxError) + return + } + args = args[2:] + case "withscores": + if store { + setDirty(c) + c.WriteError(msgSyntaxError) + return + } + opts.WithScores = true + args = args[1:] default: setDirty(c) c.WriteError(msgSyntaxError) return } - args = args[2:] - default: - setDirty(c) - c.WriteError(msgSyntaxError) - return } - } - withTx(m, c, func(c *server.Peer, ctx *connCtx) { - db := m.db(ctx.selectedDB) - db.del(destination, true) - - // We collect everything and remove all keys which turned out not to be - // present in every set. - sset := map[string]float64{} - counts := map[string]int{} - for i, key := range keys { - if !db.exists(key) { - continue + withTx(m, c, func(c *server.Peer, ctx *connCtx) { + db := m.db(ctx.selectedDB) + if opts.Store { + db.del(opts.Destination, true) } - var set map[string]float64 - switch db.t(key) { - case "set": - set = map[string]float64{} - for elem := range db.setKeys[key] { - set[elem] = 1.0 - } - case "zset": - set = db.sortedSet(key) - default: - c.WriteError(msgWrongType) - return - } - for member, score := range set { - if withWeights { - score *= weights[i] - } - counts[member]++ - old, ok := sset[member] - if !ok { - sset[member] = score + // We collect everything and remove all keys which turned out not to be + // present in every set. + sset := map[string]float64{} + counts := map[string]int{} + for i, key := range opts.Keys { + if !db.exists(key) { continue } - switch aggregate { + + var set map[string]float64 + switch db.t(key) { + case "set": + set = map[string]float64{} + for elem := range db.setKeys[key] { + set[elem] = 1.0 + } + case "zset": + set = db.sortedSet(key) default: - panic("Invalid aggregate") - case "sum": - sset[member] += score - case "min": - if score < old { - sset[member] = score + c.WriteError(msgWrongType) + return + } + for member, score := range set { + if opts.WithWeights { + score *= opts.Weights[i] } - case "max": - if score > old { + counts[member]++ + old, ok := sset[member] + if !ok { sset[member] = score + continue + } + switch opts.Aggregate { + default: + panic("Invalid aggregate") + case "sum": + sset[member] += score + case "min": + if score < old { + sset[member] = score + } + case "max": + if score > old { + sset[member] = score + } } } } - } - for key, count := range counts { - if count != numKeys { - delete(sset, key) + for key, count := range counts { + if count != numKeys { + delete(sset, key) + } } - } - db.ssetSet(destination, sset) - c.WriteInt(len(sset)) - }) + + if opts.Store { + // ZINTERSTORE mode + db.ssetSet(opts.Destination, sset) + c.WriteInt(len(sset)) + return + } + // ZINTER mode + size := len(sset) + if opts.WithScores { + size *= 2 + } + c.WriteLen(size) + for _, l := range sortedKeys(sset) { + c.WriteBulk(l) + if opts.WithScores { + c.WriteFloat(sset[l]) + } + } + }) + } } // ZLEXCOUNT @@ -761,7 +810,7 @@ func (m *Miniredis) makeCmdZrangebyscore(reverse bool) server.Cmd { // ZRANK and ZREVRANK func (m *Miniredis) makeCmdZrank(reverse bool) server.Cmd { return func(c *server.Peer, cmd string, args []string) { - if len(args) != 2 { + if len(args) < 2 { setDirty(c) c.WriteError(errWrongNumber(cmd)) return @@ -778,6 +827,12 @@ func (m *Miniredis) makeCmdZrank(reverse bool) server.Cmd { withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) + if len(args) > 2 { + setDirty(c) + c.WriteError(msgSyntaxError) + return + } + if !db.exists(key) { c.WriteNull() return @@ -1449,6 +1504,7 @@ func (m *Miniredis) cmdZscan(c *server.Peer, cmd string, args []string) { var opts struct { key string cursor int + count int withMatch bool match string } @@ -1466,12 +1522,18 @@ func (m *Miniredis) cmdZscan(c *server.Peer, cmd string, args []string) { c.WriteError(msgSyntaxError) return } - if _, err := strconv.Atoi(args[1]); err != nil { + count, err := strconv.Atoi(args[1]) + if err != nil { 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 } @@ -1493,14 +1555,6 @@ 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 opts.cursor != 0 { - // Invalid cursor. - c.WriteLen(2) - c.WriteBulk("0") // no next cursor - c.WriteLen(0) // no elements - return - } if db.exists(opts.key) && db.t(opts.key) != "zset" { c.WriteError(ErrWrongType.Error()) return @@ -1511,8 +1565,27 @@ func (m *Miniredis) cmdZscan(c *server.Peer, cmd string, args []string) { 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 + } + 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)) // HSCAN gives key, values. c.WriteLen(len(members) * 2) for _, k := range members { @@ -1922,3 +1995,12 @@ func parseLexrange(s string) (string, bool, error) { return "", false, errors.New(msgInvalidRangeItem) } } + +func sortedKeys(m map[string]float64) []string { + var keys []string + for k := range m { + keys = append(keys, k) + } + sort.Strings(keys) + return keys +} diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_stream.go b/vendor/github.com/alicebob/miniredis/v2/cmd_stream.go index 9e76138146e43..b6d00343bcc45 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_stream.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_stream.go @@ -50,6 +50,11 @@ func (m *Miniredis) cmdXadd(c *server.Peer, cmd string, args []string) { withTx(m, c, func(c *server.Peer, ctx *connCtx) { maxlen := -1 minID := "" + makeStream := true + if strings.ToLower(args[0]) == "nomkstream" { + args = args[1:] + makeStream = false + } if strings.ToLower(args[0]) == "maxlen" { args = args[1:] // we don't treat "~" special @@ -101,7 +106,10 @@ func (m *Miniredis) cmdXadd(c *server.Peer, cmd string, args []string) { return } if s == nil { - // TODO: NOMKSTREAM + if !makeStream { + c.WriteNull() + return + } s, _ = db.newStream(key) } @@ -121,7 +129,7 @@ func (m *Miniredis) cmdXadd(c *server.Peer, cmd string, args []string) { if minID != "" { s.trimBefore(minID) } - db.keyVersion[key]++ + db.incr(key) c.WriteBulk(newID) }) @@ -620,8 +628,7 @@ func (m *Miniredis) cmdXinfoConsumers(c *server.Peer, args []string) { c.WriteError(errWrongNumber("CONSUMERS")) return } - key := args[0] - groupName := args[1] + key, groupName := args[0], args[1] withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) @@ -643,7 +650,7 @@ func (m *Miniredis) cmdXinfoConsumers(c *server.Peer, args []string) { return } - consumerNames := make([]string, 0) + var consumerNames []string for name := range g.consumers { consumerNames = append(consumerNames, name) } @@ -651,17 +658,29 @@ func (m *Miniredis) cmdXinfoConsumers(c *server.Peer, args []string) { c.WriteLen(len(consumerNames)) for _, name := range consumerNames { - c.WriteMapLen(2) + cons := g.consumers[name] + c.WriteMapLen(4) c.WriteBulk("name") c.WriteBulk(name) - c.WriteBulk("pending") - c.WriteInt(g.consumers[name].numPendingEntries) + c.WriteInt(cons.numPendingEntries) + // TODO: these times aren't set for all commands + c.WriteBulk("idle") + c.WriteInt(m.sinceMilli(cons.lastSeen)) + c.WriteBulk("inactive") + c.WriteInt(m.sinceMilli(cons.lastSuccess)) } }) } +func (m *Miniredis) sinceMilli(t time.Time) int { + if t.IsZero() { + return -1 + } + return int(m.effectiveNow().Sub(t).Milliseconds()) +} + // XREADGROUP func (m *Miniredis) cmdXreadgroup(c *server.Peer, cmd string, args []string) { // XREADGROUP GROUP group consumer STREAMS key ID @@ -920,7 +939,7 @@ func (m *Miniredis) cmdXdel(c *server.Peer, cmd string, args []string) { c.WriteError(err.Error()) return } - db.keyVersion[stream]++ + db.incr(stream) c.WriteInt(n) }) } @@ -1654,7 +1673,7 @@ func (m *Miniredis) cmdXclaim(c *server.Peer, cmd string, args []string) { c.WriteError("ERR Invalid TIME option argument for XCLAIM") return } - opts.newLastDelivery = unixMilli(timeMs) + opts.newLastDelivery = time.UnixMilli(timeMs) args = args[2:] case "RETRYCOUNT": retryCount, err := strconv.Atoi(args[1]) @@ -1708,6 +1727,7 @@ func (m *Miniredis) xclaim( for _, id := range ids { pelPos, pelEntry := group.searchPending(id) if pelEntry == nil { + group.setLastSeen(consumerName, m.effectiveNow()) if !force { continue } @@ -1724,6 +1744,7 @@ func (m *Miniredis) xclaim( consumer: consumerName, deliveryCount: 1, } + group.setLastSuccess(consumerName, m.effectiveNow()) } else { group.consumers[pelEntry.consumer].numPendingEntries-- pelEntry.consumer = consumerName @@ -1744,6 +1765,7 @@ func (m *Miniredis) xclaim( claimedEntryIDs = append(claimedEntryIDs, id) } if len(claimedEntryIDs) == 0 { + group.setLastSeen(consumerName, m.effectiveNow()) return } @@ -1753,6 +1775,7 @@ func (m *Miniredis) xclaim( consumer := group.consumers[consumerName] consumer.numPendingEntries += len(claimedEntryIDs) + group.setLastSuccess(consumerName, m.effectiveNow()) return } @@ -1791,8 +1814,3 @@ 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/db.go b/vendor/github.com/alicebob/miniredis/v2/db.go index 388d5fbbd7a97..af56839fab1d3 100644 --- a/vendor/github.com/alicebob/miniredis/v2/db.go +++ b/vendor/github.com/alicebob/miniredis/v2/db.go @@ -3,6 +3,7 @@ package miniredis import ( "errors" "fmt" + "math" "math/big" "sort" "strconv" @@ -13,8 +14,12 @@ var ( errInvalidEntryID = errors.New("stream ID is invalid") ) +// exists also updates the lru func (db *RedisDB) exists(k string) bool { _, ok := db.keys[k] + if ok { + db.lru[k] = db.master.effectiveNow() + } return ok } @@ -23,6 +28,12 @@ func (db *RedisDB) t(k string) string { return db.keys[k] } +// incr increases the version and the lru timestamp +func (db *RedisDB) incr(k string) { + db.lru[k] = db.master.effectiveNow() + db.keyVersion[k]++ +} + // allKeys returns all keys. Sorted. func (db *RedisDB) allKeys() []string { res := make([]string, 0, len(db.keys)) @@ -36,6 +47,7 @@ func (db *RedisDB) allKeys() []string { // flush removes all keys and values. func (db *RedisDB) flush() { db.keys = map[string]string{} + db.lru = map[string]time.Time{} db.stringKeys = map[string]string{} db.hashKeys = map[string]hashKey{} db.listKeys = map[string]listKey{} @@ -75,10 +87,10 @@ func (db *RedisDB) move(key string, to *RedisDB) bool { default: panic("unhandled key type") } - to.keyVersion[key]++ if v, ok := db.ttl[key]; ok { to.ttl[key] = v } + to.incr(key) db.del(key, true) return true } @@ -104,10 +116,10 @@ func (db *RedisDB) rename(from, to string) { panic("missing case") } db.keys[to] = db.keys[from] - db.keyVersion[to]++ if v, ok := db.ttl[from]; ok { db.ttl[to] = v } + db.incr(to) db.del(from, true) } @@ -118,6 +130,7 @@ func (db *RedisDB) del(k string, delTTL bool) { } t := db.t(k) delete(db.keys, k) + delete(db.lru, k) db.keyVersion[k]++ if delTTL { delete(db.ttl, k) @@ -155,7 +168,7 @@ func (db *RedisDB) stringSet(k, v string) { db.del(k, false) db.keys[k] = "string" db.stringKeys[k] = v - db.keyVersion[k]++ + db.incr(k) } // change int key value @@ -168,6 +181,17 @@ func (db *RedisDB) stringIncr(k string, delta int) (int, error) { return 0, ErrIntValueError } } + + if delta > 0 { + if math.MaxInt-delta < v { + return 0, ErrIntValueOverflowError + } + } else { + if math.MinInt-delta > v { + return 0, ErrIntValueOverflowError + } + } + v += delta db.stringSet(k, strconv.Itoa(v)) return v, nil @@ -197,7 +221,7 @@ func (db *RedisDB) listLpush(k, v string) int { } l = append([]string{v}, l...) db.listKeys[k] = l - db.keyVersion[k]++ + db.incr(k) return len(l) } @@ -211,7 +235,7 @@ func (db *RedisDB) listLpop(k string) string { } else { db.listKeys[k] = l } - db.keyVersion[k]++ + db.incr(k) return el } @@ -222,7 +246,7 @@ func (db *RedisDB) listPush(k string, v ...string) int { } l = append(l, v...) db.listKeys[k] = l - db.keyVersion[k]++ + db.incr(k) return len(l) } @@ -234,7 +258,7 @@ func (db *RedisDB) listPop(k string) string { db.del(k, true) } else { db.listKeys[k] = l - db.keyVersion[k]++ + db.incr(k) } return el } @@ -243,7 +267,7 @@ func (db *RedisDB) listPop(k string) string { func (db *RedisDB) setSet(k string, set setKey) { db.keys[k] = "set" db.setKeys[k] = set - db.keyVersion[k]++ + db.incr(k) } // setadd adds members to a set. Returns nr of new keys. @@ -261,7 +285,7 @@ func (db *RedisDB) setAdd(k string, elems ...string) int { s[e] = struct{}{} } db.setKeys[k] = s - db.keyVersion[k]++ + db.incr(k) return added } @@ -283,7 +307,7 @@ func (db *RedisDB) setRem(k string, fields ...string) int { } else { db.setKeys[k] = s } - db.keyVersion[k]++ + db.incr(k) return removed } @@ -349,7 +373,7 @@ func (db *RedisDB) hashSet(k string, fv ...string) int { f, v := fv[idx], fv[idx+1] _, ok := db.hashKeys[k][f] db.hashKeys[k][f] = v - db.keyVersion[k]++ + db.incr(k) if !ok { new++ } @@ -401,7 +425,7 @@ func (db *RedisDB) sortedSet(key string) map[string]float64 { // ssetSet sets a complete sorted set. func (db *RedisDB) ssetSet(key string, sset sortedSet) { db.keys[key] = "zset" - db.keyVersion[key]++ + db.incr(key) db.sortedsetKeys[key] = sset } @@ -415,7 +439,7 @@ func (db *RedisDB) ssetAdd(key string, score float64, member string) bool { _, ok = ss[member] ss[member] = score db.sortedsetKeys[key] = ss - db.keyVersion[key]++ + db.incr(key) return !ok } @@ -509,7 +533,7 @@ func (db *RedisDB) ssetIncrby(k, m string, delta float64) float64 { v, _ := ss.get(m) v += delta ss.set(v, m) - db.keyVersion[k]++ + db.incr(k) return v } @@ -578,6 +602,54 @@ func (db *RedisDB) setInter(keys []string) (setKey, error) { return s, nil } +// setIntercard implements the logic behind SINTER* +// len keys needs to be > 0 +func (db *RedisDB) setIntercard(keys []string, limit int) (int, error) { + // all keys must either not exist, or be of type "set". + allExist := true + for _, key := range keys { + exists := db.exists(key) + allExist = allExist && exists + if exists && db.t(key) != "set" { + return 0, ErrWrongType + } + } + + if !allExist { + return 0, nil + } + + smallestKey := keys[0] + smallestIdx := 0 + for i, key := range keys { + if len(db.setKeys[key]) < len(db.setKeys[smallestKey]) { + smallestKey = key + smallestIdx = i + } + } + keys[smallestIdx] = keys[len(keys)-1] + keys = keys[:len(keys)-1] + + count := 0 + for item := range db.setKeys[smallestKey] { + inIntersection := true + for _, key := range keys { + if _, ok := db.setKeys[key][item]; !ok { + inIntersection = false + break + } + } + if inIntersection { + count++ + if count == limit { + break + } + } + } + + return count, nil +} + // setUnion implements the logic behind SUNION* func (db *RedisDB) setUnion(keys []string) (setKey, error) { key := keys[0] @@ -613,7 +685,7 @@ func (db *RedisDB) newStream(key string) (*streamKey, error) { db.keys[key] = "stream" s := newStreamKey() db.streamKeys[key] = s - db.keyVersion[key]++ + db.incr(key) return s, nil } @@ -665,7 +737,7 @@ func (db *RedisDB) hllAdd(k string, elems ...string) int { } } db.hllKeys[k] = s - db.keyVersion[k]++ + db.incr(k) return hllAltered } @@ -712,7 +784,7 @@ func (db *RedisDB) hllMerge(keys []string) error { db.hllKeys[destKey] = destHll db.keys[destKey] = "hll" - db.keyVersion[destKey]++ + db.incr(destKey) return nil } diff --git a/vendor/github.com/alicebob/miniredis/v2/direct.go b/vendor/github.com/alicebob/miniredis/v2/direct.go index 607c4e0889dbd..31505eb462249 100644 --- a/vendor/github.com/alicebob/miniredis/v2/direct.go +++ b/vendor/github.com/alicebob/miniredis/v2/direct.go @@ -21,6 +21,9 @@ var ( // ErrIntValueError can returned by INCRBY ErrIntValueError = errors.New(msgInvalidInt) + // ErrIntValueOverflowError can be returned by INCR, DECR, INCRBY, DECRBY + ErrIntValueOverflowError = errors.New(msgIntOverflow) + // ErrFloatValueError can returned by INCRBYFLOAT ErrFloatValueError = errors.New(msgInvalidFloat) ) @@ -421,7 +424,7 @@ func (db *RedisDB) SetTTL(k string, ttl time.Duration) { defer db.master.signal.Broadcast() db.ttl[k] = ttl - db.keyVersion[k]++ + db.incr(k) } // Type gives the type of a key, or "" @@ -506,7 +509,7 @@ func (db *RedisDB) hdel(k, f string) { return } delete(db.hashKeys[k], f) - db.keyVersion[k]++ + db.incr(k) } // HIncrBy increases the integer value of a hash field by delta (int). diff --git a/vendor/github.com/alicebob/miniredis/v2/fpconv/LICENSE.txt b/vendor/github.com/alicebob/miniredis/v2/fpconv/LICENSE.txt new file mode 100644 index 0000000000000..0a0af2e8fc540 --- /dev/null +++ b/vendor/github.com/alicebob/miniredis/v2/fpconv/LICENSE.txt @@ -0,0 +1,26 @@ +This code is derived from the C code in redis-7.2.0/deps/fpconv/*, which has +this license: + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/alicebob/miniredis/v2/fpconv/Makefile b/vendor/github.com/alicebob/miniredis/v2/fpconv/Makefile new file mode 100644 index 0000000000000..d32d4bdcd1865 --- /dev/null +++ b/vendor/github.com/alicebob/miniredis/v2/fpconv/Makefile @@ -0,0 +1,6 @@ +.PHONY: test fuzz +test: + go test + +fuzz: + go test -fuzz=Fuzz diff --git a/vendor/github.com/alicebob/miniredis/v2/fpconv/README.md b/vendor/github.com/alicebob/miniredis/v2/fpconv/README.md new file mode 100644 index 0000000000000..c210e6033a609 --- /dev/null +++ b/vendor/github.com/alicebob/miniredis/v2/fpconv/README.md @@ -0,0 +1,3 @@ +This is a translation of the actual C code in Redis (7.2) which does the float +-> string conversion. +Strconv does a close enough job, but we can use the exact same logic, so why not. diff --git a/vendor/github.com/alicebob/miniredis/v2/fpconv/dtoa.go b/vendor/github.com/alicebob/miniredis/v2/fpconv/dtoa.go new file mode 100644 index 0000000000000..251fc4f3b03a3 --- /dev/null +++ b/vendor/github.com/alicebob/miniredis/v2/fpconv/dtoa.go @@ -0,0 +1,286 @@ +package fpconv + +import ( + "math" +) + +var ( + fracmask uint64 = 0x000FFFFFFFFFFFFF + expmask uint64 = 0x7FF0000000000000 + hiddenbit uint64 = 0x0010000000000000 + signmask uint64 = 0x8000000000000000 + expbias int64 = 1023 + 52 + zeros = []rune("0000000000000000000000") + + tens = []uint64{ + 10000000000000000000, + 1000000000000000000, + 100000000000000000, + 10000000000000000, + 1000000000000000, + 100000000000000, + 10000000000000, + 1000000000000, + 100000000000, + 10000000000, + 1000000000, + 100000000, + 10000000, + 1000000, + 100000, + 10000, + 1000, + 100, + 10, + 1} +) + +func absv(n int) int { + if n < 0 { + return -n + } + return n +} + +func minv(a, b int) int { + if a < b { + return a + } + return b +} + +func Dtoa(d float64) string { + var ( + dest [25]rune // Note C has 24, which is broken + digits [18]rune + + str_len int = 0 + neg = false + ) + + if get_dbits(d)&signmask != 0 { + dest[0] = '-' + str_len++ + neg = true + } + + if spec := filter_special(d, dest[str_len:]); spec != 0 { + return string(dest[:str_len+spec]) + } + + var ( + k int = 0 + ndigits int = grisu2(d, &digits, &k) + ) + + str_len += emit_digits(&digits, ndigits, dest[str_len:], k, neg) + return string(dest[:str_len]) +} + +func filter_special(fp float64, dest []rune) int { + if fp == 0.0 { + dest[0] = '0' + return 1 + } + + if math.IsNaN(fp) { + dest[0] = 'n' + dest[1] = 'a' + dest[2] = 'n' + return 3 + } + if math.IsInf(fp, 0) { + dest[0] = 'i' + dest[1] = 'n' + dest[2] = 'f' + return 3 + } + return 0 +} + +func grisu2(d float64, digits *[18]rune, K *int) int { + w := build_fp(d) + + lower, upper := get_normalized_boundaries(w) + + w = normalize(w) + + var k int64 + cp := find_cachedpow10(upper.exp, &k) + + w = multiply(w, cp) + upper = multiply(upper, cp) + lower = multiply(lower, cp) + + lower.frac++ + upper.frac-- + + *K = int(-k) + + return generate_digits(w, upper, lower, digits[:], K) +} + +func emit_digits(digits *[18]rune, ndigits int, dest []rune, K int, neg bool) int { + exp := int(absv(K + ndigits - 1)) + + /* write plain integer */ + if K >= 0 && (exp < (ndigits + 7)) { + copy(dest, digits[:ndigits]) + copy(dest[ndigits:], zeros[:K]) + + return ndigits + K + } + + /* write decimal w/o scientific notation */ + if K < 0 && (K > -7 || exp < 4) { + offset := int(ndigits - absv(K)) + /* fp < 1.0 -> write leading zero */ + if offset <= 0 { + offset = -offset + dest[0] = '0' + dest[1] = '.' + copy(dest[2:], zeros[:offset]) + copy(dest[offset+2:], digits[:ndigits]) + + return ndigits + 2 + offset + + /* fp > 1.0 */ + } else { + copy(dest, digits[:offset]) + dest[offset] = '.' + copy(dest[offset+1:], digits[offset:offset+ndigits-offset]) + + return ndigits + 1 + } + } + /* write decimal w/ scientific notation */ + l := 18 // was: 18-neg + if neg { + l-- + } + ndigits = minv(ndigits, l) + + var idx int = 0 + dest[idx] = digits[0] + idx++ + + if ndigits > 1 { + dest[idx] = '.' + idx++ + copy(dest[idx:], digits[+1:ndigits-1+1]) + idx += ndigits - 1 + } + + dest[idx] = 'e' + idx++ + + sign := '+' + if K+ndigits-1 < 0 { + sign = '-' + } + dest[idx] = sign + idx++ + + var cent rune = 0 + + if exp > 99 { + cent = rune(exp / 100) + dest[idx] = cent + '0' + idx++ + exp -= int(cent) * 100 + } + if exp > 9 { + dec := rune(exp / 10) + dest[idx] = dec + '0' + idx++ + exp -= int(dec) * 10 + } else if cent != 0 { + dest[idx] = '0' + idx++ + } + + dest[idx] = rune(exp%10) + '0' + idx++ + + return idx +} + +func generate_digits(fp, upper, lower Fp, digits []rune, K *int) int { + var ( + wfrac = uint64(upper.frac - fp.frac) + delta = uint64(upper.frac - lower.frac) + ) + + one := Fp{ + frac: 1 << -upper.exp, + exp: upper.exp, + } + + part1 := uint64(upper.frac >> -one.exp) + part2 := uint64(upper.frac & (one.frac - 1)) + + var ( + idx = 0 + kappa = 10 + index = 10 + ) + /* 1000000000 */ + for ; kappa > 0; index++ { + div := tens[index] + digit := part1 / div + + if digit != 0 || idx != 0 { + digits[idx] = rune(digit) + '0' + idx++ + } + + part1 -= digit * div + kappa-- + + tmp := (part1 << -one.exp) + part2 + if tmp <= delta { + *K += kappa + round_digit(digits, idx, delta, tmp, div<<-one.exp, wfrac) + + return idx + } + } + + /* 10 */ + index = 18 + for { + var unit uint64 = tens[index] + part2 *= 10 + delta *= 10 + kappa-- + + digit := part2 >> -one.exp + if digit != 0 || idx != 0 { + digits[idx] = rune(digit) + '0' + idx++ + } + + part2 &= uint64(one.frac) - 1 + if part2 < delta { + *K += kappa + round_digit(digits, idx, delta, part2, uint64(one.frac), wfrac*unit) + + return idx + } + + index-- + } +} + +func round_digit(digits []rune, + ndigits int, + delta uint64, + rem uint64, + kappa uint64, + frac uint64) { + for rem < frac && delta-rem >= kappa && + (rem+kappa < frac || frac-rem > rem+kappa-frac) { + digits[ndigits-1]-- + rem += kappa + } +} diff --git a/vendor/github.com/alicebob/miniredis/v2/fpconv/fp.go b/vendor/github.com/alicebob/miniredis/v2/fpconv/fp.go new file mode 100644 index 0000000000000..4906463638f85 --- /dev/null +++ b/vendor/github.com/alicebob/miniredis/v2/fpconv/fp.go @@ -0,0 +1,96 @@ +package fpconv + +import ( + "math" +) + +type ( + Fp struct { + frac uint64 + exp int64 + } +) + +func build_fp(d float64) Fp { + bits := get_dbits(d) + + fp := Fp{ + frac: bits & fracmask, + exp: int64((bits & expmask) >> 52), + } + + if fp.exp != 0 { + fp.frac += hiddenbit + fp.exp -= expbias + } else { + fp.exp = -expbias + 1 + } + + return fp +} + +func normalize(fp Fp) Fp { + for (fp.frac & hiddenbit) == 0 { + fp.frac <<= 1 + fp.exp-- + } + + var shift int64 = 64 - 52 - 1 + fp.frac <<= shift + fp.exp -= shift + return fp +} + +func multiply(a, b Fp) Fp { + lomask := uint64(0x00000000FFFFFFFF) + + var ( + ah_bl = uint64((a.frac >> 32) * (b.frac & lomask)) + al_bh = uint64((a.frac & lomask) * (b.frac >> 32)) + al_bl = uint64((a.frac & lomask) * (b.frac & lomask)) + ah_bh = uint64((a.frac >> 32) * (b.frac >> 32)) + ) + + tmp := uint64((ah_bl & lomask) + (al_bh & lomask) + (al_bl >> 32)) + /* round up */ + tmp += uint64(1) << 31 + + return Fp{ + ah_bh + (ah_bl >> 32) + (al_bh >> 32) + (tmp >> 32), + a.exp + b.exp + 64, + } +} + +func get_dbits(d float64) uint64 { + return math.Float64bits(d) +} + +func get_normalized_boundaries(fp Fp) (Fp, Fp) { + upper := Fp{ + frac: (fp.frac << 1) + 1, + exp: fp.exp - 1, + } + for (upper.frac & (hiddenbit << 1)) == 0 { + upper.frac <<= 1 + upper.exp-- + } + + var u_shift int64 = 64 - 52 - 2 + + upper.frac <<= u_shift + upper.exp = upper.exp - u_shift + + l_shift := int64(1) + if fp.frac == hiddenbit { + l_shift = 2 + } + + lower := Fp{ + frac: (fp.frac << l_shift) - 1, + exp: fp.exp - l_shift, + } + + lower.frac <<= lower.exp - upper.exp + lower.exp = upper.exp + return lower, upper +} diff --git a/vendor/github.com/alicebob/miniredis/v2/fpconv/powers.go b/vendor/github.com/alicebob/miniredis/v2/fpconv/powers.go new file mode 100644 index 0000000000000..24725f9141889 --- /dev/null +++ b/vendor/github.com/alicebob/miniredis/v2/fpconv/powers.go @@ -0,0 +1,82 @@ +package fpconv + +var ( + npowers int64 = 87 + steppowers int64 = 8 + firstpower int64 = -348 /* 10 ^ -348 */ + + expmax = -32 + expmin = -60 + + powers_ten = []Fp{ + {18054884314459144840, -1220}, {13451937075301367670, -1193}, + {10022474136428063862, -1166}, {14934650266808366570, -1140}, + {11127181549972568877, -1113}, {16580792590934885855, -1087}, + {12353653155963782858, -1060}, {18408377700990114895, -1034}, + {13715310171984221708, -1007}, {10218702384817765436, -980}, + {15227053142812498563, -954}, {11345038669416679861, -927}, + {16905424996341287883, -901}, {12595523146049147757, -874}, + {9384396036005875287, -847}, {13983839803942852151, -821}, + {10418772551374772303, -794}, {15525180923007089351, -768}, + {11567161174868858868, -741}, {17236413322193710309, -715}, + {12842128665889583758, -688}, {9568131466127621947, -661}, + {14257626930069360058, -635}, {10622759856335341974, -608}, + {15829145694278690180, -582}, {11793632577567316726, -555}, + {17573882009934360870, -529}, {13093562431584567480, -502}, + {9755464219737475723, -475}, {14536774485912137811, -449}, + {10830740992659433045, -422}, {16139061738043178685, -396}, + {12024538023802026127, -369}, {17917957937422433684, -343}, + {13349918974505688015, -316}, {9946464728195732843, -289}, + {14821387422376473014, -263}, {11042794154864902060, -236}, + {16455045573212060422, -210}, {12259964326927110867, -183}, + {18268770466636286478, -157}, {13611294676837538539, -130}, + {10141204801825835212, -103}, {15111572745182864684, -77}, + {11258999068426240000, -50}, {16777216000000000000, -24}, + {12500000000000000000, 3}, {9313225746154785156, 30}, + {13877787807814456755, 56}, {10339757656912845936, 83}, + {15407439555097886824, 109}, {11479437019748901445, 136}, + {17105694144590052135, 162}, {12744735289059618216, 189}, + {9495567745759798747, 216}, {14149498560666738074, 242}, + {10542197943230523224, 269}, {15709099088952724970, 295}, + {11704190886730495818, 322}, {17440603504673385349, 348}, + {12994262207056124023, 375}, {9681479787123295682, 402}, + {14426529090290212157, 428}, {10748601772107342003, 455}, + {16016664761464807395, 481}, {11933345169920330789, 508}, + {17782069995880619868, 534}, {13248674568444952270, 561}, + {9871031767461413346, 588}, {14708983551653345445, 614}, + {10959046745042015199, 641}, {16330252207878254650, 667}, + {12166986024289022870, 694}, {18130221999122236476, 720}, + {13508068024458167312, 747}, {10064294952495520794, 774}, + {14996968138956309548, 800}, {11173611982879273257, 827}, + {16649979327439178909, 853}, {12405201291620119593, 880}, + {9242595204427927429, 907}, {13772540099066387757, 933}, + {10261342003245940623, 960}, {15290591125556738113, 986}, + {11392378155556871081, 1013}, {16975966327722178521, 1039}, + {12648080533535911531, 1066}, + } +) + +func find_cachedpow10(exp int64, k *int64) Fp { + one_log_ten := 0.30102999566398114 + + approx := int64(float64(-(exp + npowers)) * one_log_ten) + idx := int((approx - firstpower) / steppowers) + + for { + current := int(exp + powers_ten[idx].exp + 64) + + if current < expmin { + idx++ + continue + } + + if current > expmax { + idx-- + continue + } + + *k = (firstpower + int64(idx)*steppowers) + + return powers_ten[idx] + } +} diff --git a/vendor/github.com/alicebob/miniredis/v2/lua.go b/vendor/github.com/alicebob/miniredis/v2/lua.go index 7c7298cf24135..ff777a45a0920 100644 --- a/vendor/github.com/alicebob/miniredis/v2/lua.go +++ b/vendor/github.com/alicebob/miniredis/v2/lua.go @@ -161,6 +161,15 @@ func mkLua(srv *server.Server, c *server.Peer, sha string) (map[string]lua.LGFun // ignored return 1 }, + "set_repl": func(l *lua.LState) int { + top := l.GetTop() + if top != 1 { + l.Error(lua.LString("wrong number of arguments"), 1) + return 0 + } + // ignored + return 1 + }, }, luaRedisConstants } diff --git a/vendor/github.com/alicebob/miniredis/v2/miniredis.go b/vendor/github.com/alicebob/miniredis/v2/miniredis.go index 490446d47142c..a9600ef621e2a 100644 --- a/vendor/github.com/alicebob/miniredis/v2/miniredis.go +++ b/vendor/github.com/alicebob/miniredis/v2/miniredis.go @@ -25,6 +25,7 @@ import ( "sync" "time" + "github.com/alicebob/miniredis/v2/proto" "github.com/alicebob/miniredis/v2/server" ) @@ -47,6 +48,7 @@ type RedisDB struct { sortedsetKeys map[string]sortedSet // ZADD &c. keys streamKeys map[string]*streamKey // XADD &c. keys ttl map[string]time.Duration // effective TTL values + lru map[string]time.Time // last recently used ( read or written to ) keyVersion map[string]uint // used to watch values } @@ -105,6 +107,7 @@ func newRedisDB(id int, m *Miniredis) RedisDB { id: id, master: m, keys: map[string]string{}, + lru: map[string]time.Time{}, stringKeys: map[string]string{}, hashKeys: map[string]hashKey{}, listKeys: map[string]listKey{}, @@ -133,6 +136,7 @@ func RunTLS(cfg *tls.Config) (*Miniredis, error) { type Tester interface { Fatalf(string, ...interface{}) Cleanup(func()) + Logf(format string, args ...interface{}) } // RunT start a new miniredis, pass it a testing.T. It also registers the cleanup after your test is done. @@ -146,6 +150,22 @@ func RunT(t Tester) *Miniredis { return m } +func runWithClient(t Tester) (*Miniredis, *proto.Client) { + m := RunT(t) + + c, err := proto.Dial(m.Addr()) + if err != nil { + t.Fatalf("could not connect to miniredis: %s", err) + } + t.Cleanup(func() { + if err = c.Close(); err != nil { + t.Logf("error closing connection to miniredis: %s", err) + } + }) + + return m, c +} + // Start starts a server. It listens on a random port on localhost. See also // Addr(). func (m *Miniredis) Start() error { @@ -175,6 +195,15 @@ func (m *Miniredis) StartAddr(addr string) error { return m.start(s) } +// StartAddrTLS runs miniredis with a given addr, TLS version. +func (m *Miniredis) StartAddrTLS(addr string, cfg *tls.Config) error { + s, err := server.NewServerTLS(addr, cfg) + if err != nil { + return err + } + return m.start(s) +} + func (m *Miniredis) start(s *server.Server) error { m.Lock() defer m.Unlock() @@ -196,6 +225,8 @@ func (m *Miniredis) start(s *server.Server) error { commandsGeo(m) commandsCluster(m) commandsHll(m) + commandsClient(m) + commandsObject(m) return nil } @@ -690,7 +721,7 @@ func (m *Miniredis) copy( panic("missing case") } destDB.keys[dst] = srcDB.keys[src] - destDB.keyVersion[dst]++ + destDB.incr(dst) if v, ok := srcDB.ttl[src]; ok { destDB.ttl[dst] = v } diff --git a/vendor/github.com/alicebob/miniredis/v2/opts.go b/vendor/github.com/alicebob/miniredis/v2/opts.go index de91386015dcd..5b29c78c23f36 100644 --- a/vendor/github.com/alicebob/miniredis/v2/opts.go +++ b/vendor/github.com/alicebob/miniredis/v2/opts.go @@ -1,6 +1,7 @@ package miniredis import ( + "errors" "math" "strconv" "time" @@ -26,6 +27,16 @@ func optIntErr(c *server.Peer, src string, dest *int, errMsg string) bool { return true } +// optIntSimple sets dest or returns an error +func optIntSimple(src string, dest *int) error { + n, err := strconv.Atoi(src) + if err != nil { + return errors.New(msgInvalidInt) + } + *dest = n + return nil +} + func optDuration(c *server.Peer, src string, dest *time.Duration) bool { n, err := strconv.ParseFloat(src, 64) if err != nil { @@ -33,9 +44,14 @@ func optDuration(c *server.Peer, src string, dest *time.Duration) bool { c.WriteError(msgInvalidTimeout) return false } - if n < 0 || math.IsInf(n, 0) { + if n < 0 { + setDirty(c) + c.WriteError(msgTimeoutNegative) + return false + } + if math.IsInf(n, 0) { setDirty(c) - c.WriteError(msgNegTimeout) + c.WriteError(msgTimeoutIsOutOfRange) return false } diff --git a/vendor/github.com/alicebob/miniredis/v2/proto/Makefile b/vendor/github.com/alicebob/miniredis/v2/proto/Makefile new file mode 100644 index 0000000000000..b9ef39496bf1d --- /dev/null +++ b/vendor/github.com/alicebob/miniredis/v2/proto/Makefile @@ -0,0 +1,2 @@ +test: + go test diff --git a/vendor/github.com/alicebob/miniredis/v2/proto/client.go b/vendor/github.com/alicebob/miniredis/v2/proto/client.go new file mode 100644 index 0000000000000..92f57baf1ea5b --- /dev/null +++ b/vendor/github.com/alicebob/miniredis/v2/proto/client.go @@ -0,0 +1,60 @@ +package proto + +import ( + "bufio" + "crypto/tls" + "net" +) + +type Client struct { + c net.Conn + r *bufio.Reader +} + +func Dial(addr string) (*Client, error) { + c, err := net.Dial("tcp", addr) + if err != nil { + return nil, err + } + + return &Client{ + c: c, + r: bufio.NewReader(c), + }, nil +} + +func DialTLS(addr string, cfg *tls.Config) (*Client, error) { + c, err := tls.Dial("tcp", addr, cfg) + if err != nil { + return nil, err + } + + return &Client{ + c: c, + r: bufio.NewReader(c), + }, nil +} + +func (c *Client) Close() error { + return c.c.Close() +} + +func (c *Client) Do(cmd ...string) (string, error) { + if err := Write(c.c, cmd); err != nil { + return "", err + } + return Read(c.r) +} + +func (c *Client) Read() (string, error) { + return Read(c.r) +} + +// Do() + ReadStrings() +func (c *Client) DoStrings(cmd ...string) ([]string, error) { + res, err := c.Do(cmd...) + if err != nil { + return nil, err + } + return ReadStrings(res) +} diff --git a/vendor/github.com/alicebob/miniredis/v2/proto/proto.go b/vendor/github.com/alicebob/miniredis/v2/proto/proto.go new file mode 100644 index 0000000000000..e378faf187c0f --- /dev/null +++ b/vendor/github.com/alicebob/miniredis/v2/proto/proto.go @@ -0,0 +1,288 @@ +package proto + +import ( + "bufio" + "errors" + "fmt" + "io" + "strconv" + "strings" +) + +var ( + ErrProtocol = errors.New("unsupported protocol") + ErrUnexpected = errors.New("not what you asked for") +) + +func readLine(r *bufio.Reader) (string, error) { + line, err := r.ReadString('\n') + if err != nil { + return "", err + } + if len(line) < 3 { + return "", ErrProtocol + } + return line, nil +} + +// Read an array, with all elements are the raw redis commands +// Also reads sets and maps. +func ReadArray(b string) ([]string, error) { + r := bufio.NewReader(strings.NewReader(b)) + line, err := readLine(r) + if err != nil { + return nil, err + } + + elems := 0 + switch line[0] { + default: + return nil, ErrUnexpected + case '*', '>', '~': + // *: array + // >: push data + // ~: set + length, err := strconv.Atoi(line[1 : len(line)-2]) + if err != nil { + return nil, err + } + elems = length + case '%': + // we also read maps. + length, err := strconv.Atoi(line[1 : len(line)-2]) + if err != nil { + return nil, err + } + elems = length * 2 + } + + var res []string + for i := 0; i < elems; i++ { + next, err := Read(r) + if err != nil { + return nil, err + } + res = append(res, next) + } + return res, nil +} + +func ReadString(b string) (string, error) { + r := bufio.NewReader(strings.NewReader(b)) + line, err := readLine(r) + if err != nil { + return "", err + } + + switch line[0] { + default: + return "", ErrUnexpected + case '$': + // bulk strings are: `$5\r\nhello\r\n` + length, err := strconv.Atoi(line[1 : len(line)-2]) + if err != nil { + return "", err + } + if length < 0 { + // -1 is a nil response + return line, nil + } + var ( + buf = make([]byte, length+2) + pos = 0 + ) + for pos < length+2 { + n, err := r.Read(buf[pos:]) + if err != nil { + return "", err + } + pos += n + } + return string(buf[:len(buf)-2]), nil + } +} + +func readInline(b string) (string, error) { + if len(b) < 3 { + return "", ErrUnexpected + } + return b[1 : len(b)-2], nil +} + +func ReadError(b string) (string, error) { + if len(b) < 1 { + return "", ErrUnexpected + } + + switch b[0] { + default: + return "", ErrUnexpected + case '-': + return readInline(b) + } +} + +func ReadStrings(b string) ([]string, error) { + elems, err := ReadArray(b) + if err != nil { + return nil, err + } + var res []string + for _, e := range elems { + s, err := ReadString(e) + if err != nil { + return nil, err + } + res = append(res, s) + } + return res, nil +} + +// Read a single command, returning it raw. Used to read replies from redis. +// Understands RESP3 proto. +func Read(r *bufio.Reader) (string, error) { + line, err := readLine(r) + if err != nil { + return "", err + } + + switch line[0] { + default: + return "", ErrProtocol + case '+', '-', ':', ',', '_': + // +: inline string + // -: errors + // :: integer + // ,: float + // _: null + // Simple line based replies. + return line, nil + case '$': + // bulk strings are: `$5\r\nhello\r\n` + length, err := strconv.Atoi(line[1 : len(line)-2]) + if err != nil { + return "", err + } + if length < 0 { + // -1 is a nil response + return line, nil + } + var ( + buf = make([]byte, length+2) + pos = 0 + ) + for pos < length+2 { + n, err := r.Read(buf[pos:]) + if err != nil { + return "", err + } + pos += n + } + return line + string(buf), nil + case '*', '>', '~': + // arrays are: `*6\r\n...` + // pushdata is: `>6\r\n...` + // sets are: `~6\r\n...` + length, err := strconv.Atoi(line[1 : len(line)-2]) + if err != nil { + return "", err + } + for i := 0; i < length; i++ { + next, err := Read(r) + if err != nil { + return "", err + } + line += next + } + return line, nil + case '%': + // maps are: `%3\r\n...` + length, err := strconv.Atoi(line[1 : len(line)-2]) + if err != nil { + return "", err + } + for i := 0; i < length*2; i++ { + next, err := Read(r) + if err != nil { + return "", err + } + line += next + } + return line, nil + } +} + +// Write a command in RESP3 proto. Used to write commands to redis. +// Currently only supports string arrays. +func Write(w io.Writer, cmd []string) error { + if _, err := fmt.Fprintf(w, "*%d\r\n", len(cmd)); err != nil { + return err + } + for _, c := range cmd { + if _, err := fmt.Fprintf(w, "$%d\r\n%s\r\n", len(c), c); err != nil { + return err + } + } + return nil +} + +// Parse into interfaces. `b` must contain exactly a single command (which can be nested). +func Parse(b string) (interface{}, error) { + if len(b) < 1 { + return nil, ErrUnexpected + } + + switch b[0] { + default: + return "", ErrProtocol + case '+': + return readInline(b) + case '-': + e, err := readInline(b) + if err != nil { + return nil, err + } + return errors.New(e), nil + case ':': + e, err := readInline(b) + if err != nil { + return nil, err + } + return strconv.Atoi(e) + case '$': + return ReadString(b) + case '*': + elems, err := ReadArray(b) + if err != nil { + return nil, err + } + var res []interface{} + for _, elem := range elems { + e, err := Parse(elem) + if err != nil { + return nil, err + } + res = append(res, e) + } + return res, nil + case '%': + elems, err := ReadArray(b) + if err != nil { + return nil, err + } + var res = map[interface{}]interface{}{} + for len(elems) > 1 { + key, err := Parse(elems[0]) + if err != nil { + return nil, err + } + value, err := Parse(elems[1]) + if err != nil { + return nil, err + } + res[key] = value + elems = elems[2:] + } + return res, nil + } +} diff --git a/vendor/github.com/alicebob/miniredis/v2/proto/types.go b/vendor/github.com/alicebob/miniredis/v2/proto/types.go new file mode 100644 index 0000000000000..0b3b7c9af2295 --- /dev/null +++ b/vendor/github.com/alicebob/miniredis/v2/proto/types.go @@ -0,0 +1,102 @@ +package proto + +import ( + "fmt" + "strings" +) + +// Byte-safe string +func String(s string) string { + return fmt.Sprintf("$%d\r\n%s\r\n", len(s), s) +} + +// Inline string +func Inline(s string) string { + return inline('+', s) +} + +// Error +func Error(s string) string { + return inline('-', s) +} + +func inline(r rune, s string) string { + return fmt.Sprintf("%s%s\r\n", string(r), s) +} + +// Int +func Int(n int) string { + return fmt.Sprintf(":%d\r\n", n) +} + +// Float +func Float(n float64) string { + return fmt.Sprintf(",%g\r\n", n) +} + +const ( + Nil = "$-1\r\n" + NilResp3 = "_\r\n" + NilList = "*-1\r\n" +) + +// Array assembles the args in a list. Args should be raw redis commands. +// Example: Array(String("foo"), String("bar")) +func Array(args ...string) string { + return fmt.Sprintf("*%d\r\n", len(args)) + strings.Join(args, "") +} + +// Push assembles the args for push-data. Args should be raw redis commands. +// Example: Push(String("foo"), String("bar")) +func Push(args ...string) string { + return fmt.Sprintf(">%d\r\n", len(args)) + strings.Join(args, "") +} + +// Strings is a helper to build 1 dimensional string arrays. +func Strings(args ...string) string { + var strings []string + for _, a := range args { + strings = append(strings, String(a)) + } + return Array(strings...) +} + +// Ints is a helper to build 1 dimensional int arrays. +func Ints(args ...int) string { + var ints []string + for _, a := range args { + ints = append(ints, Int(a)) + } + return Array(ints...) +} + +// Map assembles the args in a map. Args should be raw redis commands. +// Must be an even number of arguments. +// Example: Map(String("foo"), String("bar")) +func Map(args ...string) string { + return fmt.Sprintf("%%%d\r\n", len(args)/2) + strings.Join(args, "") +} + +// StringMap is is a wrapper to get a map of (bulk)strings. +func StringMap(args ...string) string { + var strings []string + for _, a := range args { + strings = append(strings, String(a)) + } + return Map(strings...) +} + +// Set assembles the args in a map. Args should be raw redis commands. +// Example: Set(String("foo"), String("bar")) +func Set(args ...string) string { + return fmt.Sprintf("~%d\r\n", len(args)) + strings.Join(args, "") +} + +// StringSet is is a wrapper to get a set of (bulk)strings. +func StringSet(args ...string) string { + var strings []string + for _, a := range args { + strings = append(strings, String(a)) + } + return Set(strings...) +} diff --git a/vendor/github.com/alicebob/miniredis/v2/redis.go b/vendor/github.com/alicebob/miniredis/v2/redis.go index 6cc52db0a128a..f70728e7c73cc 100644 --- a/vendor/github.com/alicebob/miniredis/v2/redis.go +++ b/vendor/github.com/alicebob/miniredis/v2/redis.go @@ -16,6 +16,7 @@ const ( msgWrongType = "WRONGTYPE Operation against a key holding the wrong kind of value" msgNotValidHllValue = "WRONGTYPE Key is not a valid HyperLogLog string value." msgInvalidInt = "ERR value is not an integer or out of range" + msgIntOverflow = "ERR increment or decrement would overflow" msgInvalidFloat = "ERR value is not a valid float" msgInvalidMinMax = "ERR min or max is not a float" msgInvalidRangeItem = "ERR min or max not valid string range item" @@ -26,7 +27,8 @@ const ( msgOutOfRange = "ERR index out of range" msgInvalidCursor = "ERR invalid cursor" msgXXandNX = "ERR XX and NX options at the same time are not compatible" - msgNegTimeout = "ERR timeout is negative" + msgTimeoutNegative = "ERR timeout is negative" + msgTimeoutIsOutOfRange = "ERR timeout is out of range" msgInvalidSETime = "ERR invalid expire time in set" msgInvalidSETEXTime = "ERR invalid expire time in setex" msgInvalidPSETEXTime = "ERR invalid expire time in psetex" @@ -36,6 +38,7 @@ const ( msgFScriptUsageSimple = "ERR unknown subcommand '%s'. Try SCRIPT HELP." msgFPubsubUsage = "ERR unknown subcommand or wrong number of arguments for '%s'. Try PUBSUB HELP." msgFPubsubUsageSimple = "ERR unknown subcommand '%s'. Try PUBSUB HELP." + msgFObjectUsage = "ERR unknown subcommand '%s'. Try OBJECT HELP." msgScriptFlush = "ERR SCRIPT FLUSH only support SYNC|ASYNC option" msgSingleElementPair = "ERR INCR option supports a single increment-element pair" msgGTLTandNX = "ERR GT, LT, and/or NX options at the same time are not compatible" @@ -43,8 +46,8 @@ const ( msgStreamIDTooSmall = "ERR The ID specified in XADD is equal or smaller than the target stream top item" msgStreamIDZero = "ERR The ID specified in XADD must be greater than 0-0" msgNoScriptFound = "NOSCRIPT No matching script. Please use EVAL." - msgUnsupportedUnit = "ERR unsupported unit provided. please use m, km, ft, mi" - msgXreadUnbalanced = "ERR Unbalanced XREAD list of streams: for each stream key an ID or '$' must be specified." + msgUnsupportedUnit = "ERR unsupported unit provided. please use M, KM, FT, MI" + msgXreadUnbalanced = "ERR Unbalanced 'xread' list of streams: for each stream key an ID or '$' must be specified." msgXgroupKeyNotFound = "ERR The XGROUP subcommand requires the key to exist. Note that for CREATE you may want to use the MKSTREAM option to create an empty stream automatically." msgXtrimInvalidStrategy = "ERR unsupported XTRIM strategy. Please use MAXLEN, MINID" msgXtrimInvalidMaxLen = "ERR value is not an integer or out of range" @@ -54,6 +57,8 @@ const ( 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" + msgLimitIsNegative = "ERR LIMIT can't be negative" + msgMemorySubcommand = "ERR unknown subcommand '%s'. Try MEMORY HELP." ) func errWrongNumber(cmd string) string { diff --git a/vendor/github.com/alicebob/miniredis/v2/server/server.go b/vendor/github.com/alicebob/miniredis/v2/server/server.go index ee4f04c2186e0..f425675f97dac 100644 --- a/vendor/github.com/alicebob/miniredis/v2/server/server.go +++ b/vendor/github.com/alicebob/miniredis/v2/server/server.go @@ -4,11 +4,12 @@ import ( "bufio" "crypto/tls" "fmt" - "math" "net" "strings" "sync" "unicode" + + "github.com/alicebob/miniredis/v2/fpconv" ) func errUnknownCommand(cmd string, args []string) string { @@ -247,6 +248,7 @@ type Peer struct { Ctx interface{} // anything goes, server won't touch this onDisconnect []func() // list of callbacks mu sync.Mutex // for Block() + ClientName string // client name set by CLIENT SETNAME } func NewPeer(w *bufio.Writer) *Peer { @@ -476,29 +478,8 @@ func (w *Writer) Flush() { w.w.Flush() } -// formatFloat formats a float the way redis does (sort-of) +// formatFloat formats a float the way redis does. +// Redis uses a method called "grisu2", which we ported from C. func formatFloat(v float64) string { - if math.IsInf(v, 1) { - return "inf" - } - if math.IsInf(v, -1) { - return "-inf" - } - return stripZeros(fmt.Sprintf("%.12f", v)) -} - -func stripZeros(sv string) string { - for strings.Contains(sv, ".") { - if sv[len(sv)-1] != '0' { - break - } - // Remove trailing 0s. - sv = sv[:len(sv)-1] - // Ends with a '.'. - if sv[len(sv)-1] == '.' { - sv = sv[:len(sv)-1] - break - } - } - return sv + return fpconv.Dtoa(v) } diff --git a/vendor/github.com/alicebob/miniredis/v2/size/readme.md b/vendor/github.com/alicebob/miniredis/v2/size/readme.md new file mode 100644 index 0000000000000..89220e459adc2 --- /dev/null +++ b/vendor/github.com/alicebob/miniredis/v2/size/readme.md @@ -0,0 +1,2 @@ + +Credits to DmitriyVTitov on his package https://github.com/DmitriyVTitov/size diff --git a/vendor/github.com/alicebob/miniredis/v2/size/size.go b/vendor/github.com/alicebob/miniredis/v2/size/size.go new file mode 100644 index 0000000000000..43fee6e216a75 --- /dev/null +++ b/vendor/github.com/alicebob/miniredis/v2/size/size.go @@ -0,0 +1,138 @@ +package size + +import ( + "reflect" + "unsafe" +) + +// Of returns the size of 'v' in bytes. +// If there is an error during calculation, Of returns -1. +func Of(v interface{}) int { + // Cache with every visited pointer so we don't count two pointers + // to the same memory twice. + cache := make(map[uintptr]bool) + return sizeOf(reflect.Indirect(reflect.ValueOf(v)), cache) +} + +// sizeOf returns the number of bytes the actual data represented by v occupies in memory. +// If there is an error, sizeOf returns -1. +func sizeOf(v reflect.Value, cache map[uintptr]bool) int { + switch v.Kind() { + + case reflect.Array: + sum := 0 + for i := 0; i < v.Len(); i++ { + s := sizeOf(v.Index(i), cache) + if s < 0 { + return -1 + } + sum += s + } + + return sum + (v.Cap()-v.Len())*int(v.Type().Elem().Size()) + + case reflect.Slice: + // return 0 if this node has been visited already + if cache[v.Pointer()] { + return 0 + } + cache[v.Pointer()] = true + + sum := 0 + for i := 0; i < v.Len(); i++ { + s := sizeOf(v.Index(i), cache) + if s < 0 { + return -1 + } + sum += s + } + + sum += (v.Cap() - v.Len()) * int(v.Type().Elem().Size()) + + return sum + int(v.Type().Size()) + + case reflect.Struct: + sum := 0 + for i, n := 0, v.NumField(); i < n; i++ { + s := sizeOf(v.Field(i), cache) + if s < 0 { + return -1 + } + sum += s + } + + // Look for struct padding. + padding := int(v.Type().Size()) + for i, n := 0, v.NumField(); i < n; i++ { + padding -= int(v.Field(i).Type().Size()) + } + + return sum + padding + + case reflect.String: + s := v.String() + hdr := (*reflect.StringHeader)(unsafe.Pointer(&s)) + if cache[hdr.Data] { + return int(v.Type().Size()) + } + cache[hdr.Data] = true + return len(s) + int(v.Type().Size()) + + case reflect.Ptr: + // return Ptr size if this node has been visited already (infinite recursion) + if cache[v.Pointer()] { + return int(v.Type().Size()) + } + cache[v.Pointer()] = true + if v.IsNil() { + return int(reflect.New(v.Type()).Type().Size()) + } + s := sizeOf(reflect.Indirect(v), cache) + if s < 0 { + return -1 + } + return s + int(v.Type().Size()) + + case reflect.Bool, + reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Int, reflect.Uint, + reflect.Chan, + reflect.Uintptr, + reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128, + reflect.Func: + return int(v.Type().Size()) + + case reflect.Map: + // return 0 if this node has been visited already (infinite recursion) + if cache[v.Pointer()] { + return 0 + } + cache[v.Pointer()] = true + sum := 0 + keys := v.MapKeys() + for i := range keys { + val := v.MapIndex(keys[i]) + // calculate size of key and value separately + sv := sizeOf(val, cache) + if sv < 0 { + return -1 + } + sum += sv + sk := sizeOf(keys[i], cache) + if sk < 0 { + return -1 + } + sum += sk + } + // Include overhead due to unused map buckets. 10.79 comes + // from https://golang.org/src/runtime/map.go. + return sum + int(v.Type().Size()) + int(float64(len(keys))*10.79) + + case reflect.Interface: + return sizeOf(v.Elem(), cache) + int(v.Type().Size()) + + } + + return -1 +} diff --git a/vendor/github.com/alicebob/miniredis/v2/stream.go b/vendor/github.com/alicebob/miniredis/v2/stream.go index 9e04aa17057a5..55faa5e1c16f0 100644 --- a/vendor/github.com/alicebob/miniredis/v2/stream.go +++ b/vendor/github.com/alicebob/miniredis/v2/stream.go @@ -38,7 +38,9 @@ type streamGroup struct { type consumer struct { numPendingEntries int - // TODO: "last seen" timestamp + // these timestamps aren't tracked perfectly + lastSeen time.Time // "idle" XINFO key + lastSuccess time.Time // "inactive" XINFO key } type pendingEntry struct { @@ -482,3 +484,17 @@ func (g *streamGroup) copy() *streamGroup { consumers: cns, } } + +func (g *streamGroup) setLastSeen(c string, t time.Time) { + cons, ok := g.consumers[c] + if !ok { + cons = &consumer{} + } + cons.lastSeen = t + g.consumers[c] = cons +} + +func (g *streamGroup) setLastSuccess(c string, t time.Time) { + g.setLastSeen(c, t) + g.consumers[c].lastSuccess = t +} diff --git a/vendor/github.com/yuin/gopher-lua/.gitignore b/vendor/github.com/yuin/gopher-lua/.gitignore new file mode 100644 index 0000000000000..485dee64bcfb4 --- /dev/null +++ b/vendor/github.com/yuin/gopher-lua/.gitignore @@ -0,0 +1 @@ +.idea diff --git a/vendor/github.com/yuin/gopher-lua/_state.go b/vendor/github.com/yuin/gopher-lua/_state.go index 960e8810e4d3f..6e9febfb6d0e1 100644 --- a/vendor/github.com/yuin/gopher-lua/_state.go +++ b/vendor/github.com/yuin/gopher-lua/_state.go @@ -398,17 +398,18 @@ func (rg *registry) forceResize(newSize int) { copy(newSlice, rg.array[:rg.top]) // should we copy the area beyond top? there shouldn't be any valid values there so it shouldn't be necessary. rg.array = newSlice } -func (rg *registry) SetTop(top int) { - // +inline-call rg.checkSize top - oldtop := rg.top - rg.top = top - for i := oldtop; i < rg.top; i++ { + +func (rg *registry) SetTop(topi int) { // +inline-start + // +inline-call rg.checkSize topi + oldtopi := rg.top + rg.top = topi + for i := oldtopi; i < rg.top; i++ { rg.array[i] = LNil } // values beyond top don't need to be valid LValues, so setting them to nil is fine // setting them to nil rather than LNil lets us invoke the golang memclr opto - if rg.top < oldtop { - nilRange := rg.array[rg.top:oldtop] + if rg.top < oldtopi { + nilRange := rg.array[rg.top:oldtopi] for i := range nilRange { nilRange[i] = nil } @@ -416,7 +417,7 @@ func (rg *registry) SetTop(top int) { //for i := rg.top; i < oldtop; i++ { // rg.array[i] = LNil //} -} +} // +inline-end func (rg *registry) Top() int { return rg.top @@ -498,34 +499,34 @@ func (rg *registry) FillNil(regm, n int) { // +inline-start func (rg *registry) Insert(value LValue, reg int) { top := rg.Top() if reg >= top { - rg.Set(reg, value) + // +inline-call rg.Set reg value return } top-- for ; top >= reg; top-- { // FIXME consider using copy() here if Insert() is called enough - rg.Set(top+1, rg.Get(top)) + // +inline-call rg.Set top+1 rg.Get(top) } - rg.Set(reg, value) + // +inline-call rg.Set reg value } -func (rg *registry) Set(reg int, val LValue) { - newSize := reg + 1 +func (rg *registry) Set(regi int, vali LValue) { // +inline-start + newSize := regi + 1 // +inline-call rg.checkSize newSize - rg.array[reg] = val - if reg >= rg.top { - rg.top = reg + 1 + rg.array[regi] = vali + if regi >= rg.top { + rg.top = regi + 1 } -} +} // +inline-end -func (rg *registry) SetNumber(reg int, val LNumber) { - newSize := reg + 1 +func (rg *registry) SetNumber(regi int, vali LNumber) { // +inline-start + newSize := regi + 1 // +inline-call rg.checkSize newSize - rg.array[reg] = rg.alloc.LNumber2I(val) - if reg >= rg.top { - rg.top = reg + 1 + rg.array[regi] = rg.alloc.LNumber2I(vali) + if regi >= rg.top { + rg.top = regi + 1 } -} +} // +inline-end func (rg *registry) IsFull() bool { return rg.top >= cap(rg.array) @@ -769,6 +770,9 @@ func (ls *LState) isStarted() bool { func (ls *LState) kill() { ls.Dead = true + if ls.ctxCancelFn != nil { + ls.ctxCancelFn() + } } func (ls *LState) indexToReg(idx int) int { @@ -1402,6 +1406,7 @@ func (ls *LState) NewThread() (*LState, context.CancelFunc) { if ls.ctx != nil { thread.mainLoop = mainLoopWithContext thread.ctx, f = context.WithCancel(ls.ctx) + thread.ctxCancelFn = f } return thread, f } @@ -1851,6 +1856,9 @@ func (ls *LState) PCall(nargs, nret int, errfunc *LFunction) (err error) { err = rcv.(*ApiError) err.(*ApiError).StackTrace = ls.stackTrace(0) } + ls.stack.SetSp(sp) + ls.currentFrame = ls.stack.Last() + ls.reg.SetTop(base) } }() ls.Call(1, 1) diff --git a/vendor/github.com/yuin/gopher-lua/_vm.go b/vendor/github.com/yuin/gopher-lua/_vm.go index 687fe797a6327..ee2be04025167 100644 --- a/vendor/github.com/yuin/gopher-lua/_vm.go +++ b/vendor/github.com/yuin/gopher-lua/_vm.go @@ -173,7 +173,8 @@ func init() { A := int(inst>>18) & 0xff //GETA RA := lbase + A B := int(inst & 0x1ff) //GETB - reg.Set(RA, reg.Get(lbase+B)) + v := reg.Get(lbase + B) + // +inline-call reg.Set RA v return 0 }, func(L *LState, inst uint32, baseframe *callFrame) int { //OP_MOVEN @@ -183,7 +184,8 @@ func init() { A := int(inst>>18) & 0xff //GETA B := int(inst & 0x1ff) //GETB C := int(inst>>9) & 0x1ff //GETC - reg.Set(lbase+A, reg.Get(lbase+B)) + v := reg.Get(lbase + B) + // +inline-call reg.Set lbase+A v code := cf.Fn.Proto.Code pc := cf.Pc for i := 0; i < C; i++ { @@ -191,7 +193,8 @@ func init() { pc++ A = int(inst>>18) & 0xff //GETA B = int(inst & 0x1ff) //GETB - reg.Set(lbase+A, reg.Get(lbase+B)) + v := reg.Get(lbase + B) + // +inline-call reg.Set lbase+A v } cf.Pc = pc return 0 @@ -203,7 +206,8 @@ func init() { A := int(inst>>18) & 0xff //GETA RA := lbase + A Bx := int(inst & 0x3ffff) //GETBX - reg.Set(RA, cf.Fn.Proto.Constants[Bx]) + v := cf.Fn.Proto.Constants[Bx] + // +inline-call reg.Set RA v return 0 }, func(L *LState, inst uint32, baseframe *callFrame) int { //OP_LOADBOOL @@ -215,9 +219,9 @@ func init() { B := int(inst & 0x1ff) //GETB C := int(inst>>9) & 0x1ff //GETC if B != 0 { - reg.Set(RA, LTrue) + // +inline-call reg.Set RA LTrue } else { - reg.Set(RA, LFalse) + // +inline-call reg.Set RA LFalse } if C != 0 { cf.Pc++ @@ -232,7 +236,7 @@ func init() { RA := lbase + A B := int(inst & 0x1ff) //GETB for i := RA; i <= lbase+B; i++ { - reg.Set(i, LNil) + // +inline-call reg.Set i LNil } return 0 }, @@ -243,7 +247,8 @@ func init() { A := int(inst>>18) & 0xff //GETA RA := lbase + A B := int(inst & 0x1ff) //GETB - reg.Set(RA, cf.Fn.Upvalues[B].Value()) + v := cf.Fn.Upvalues[B].Value() + // +inline-call reg.Set RA v return 0 }, func(L *LState, inst uint32, baseframe *callFrame) int { //OP_GETGLOBAL @@ -254,7 +259,8 @@ func init() { RA := lbase + A Bx := int(inst & 0x3ffff) //GETBX //reg.Set(RA, L.getField(cf.Fn.Env, cf.Fn.Proto.Constants[Bx])) - reg.Set(RA, L.getFieldString(cf.Fn.Env, cf.Fn.Proto.stringConstants[Bx])) + v := L.getFieldString(cf.Fn.Env, cf.Fn.Proto.stringConstants[Bx]) + // +inline-call reg.Set RA v return 0 }, func(L *LState, inst uint32, baseframe *callFrame) int { //OP_GETTABLE @@ -265,7 +271,8 @@ func init() { RA := lbase + A B := int(inst & 0x1ff) //GETB C := int(inst>>9) & 0x1ff //GETC - reg.Set(RA, L.getField(reg.Get(lbase+B), L.rkValue(C))) + v := L.getField(reg.Get(lbase+B), L.rkValue(C)) + // +inline-call reg.Set RA v return 0 }, func(L *LState, inst uint32, baseframe *callFrame) int { //OP_GETTABLEKS @@ -276,7 +283,8 @@ func init() { RA := lbase + A B := int(inst & 0x1ff) //GETB C := int(inst>>9) & 0x1ff //GETC - reg.Set(RA, L.getFieldString(reg.Get(lbase+B), L.rkString(C))) + v := L.getFieldString(reg.Get(lbase+B), L.rkString(C)) + // +inline-call reg.Set RA v return 0 }, func(L *LState, inst uint32, baseframe *callFrame) int { //OP_SETGLOBAL @@ -330,7 +338,8 @@ func init() { RA := lbase + A B := int(inst & 0x1ff) //GETB C := int(inst>>9) & 0x1ff //GETC - reg.Set(RA, newLTable(B, C)) + v := newLTable(B, C) + // +inline-call reg.Set RA v return 0 }, func(L *LState, inst uint32, baseframe *callFrame) int { //OP_SELF @@ -342,8 +351,9 @@ func init() { B := int(inst & 0x1ff) //GETB C := int(inst>>9) & 0x1ff //GETC selfobj := reg.Get(lbase + B) - reg.Set(RA, L.getFieldString(selfobj, L.rkString(C))) - reg.Set(RA+1, selfobj) + v := L.getFieldString(selfobj, L.rkString(C)) + // +inline-call reg.Set RA v + // +inline-call reg.Set RA+1 selfobj return 0 }, opArith, // OP_ADD @@ -361,17 +371,17 @@ func init() { B := int(inst & 0x1ff) //GETB unaryv := L.rkValue(B) if nm, ok := unaryv.(LNumber); ok { - reg.SetNumber(RA, -nm) + // +inline-call reg.Set RA -nm } else { op := L.metaOp1(unaryv, "__unm") if op.Type() == LTFunction { reg.Push(op) reg.Push(unaryv) L.Call(1, 1) - reg.Set(RA, reg.Pop()) + // +inline-call reg.Set RA reg.Pop() } else if str, ok1 := unaryv.(LString); ok1 { if num, err := parseNumber(string(str)); err == nil { - reg.Set(RA, -num) + // +inline-call reg.Set RA -num } else { L.RaiseError("__unm undefined") } @@ -389,9 +399,9 @@ func init() { RA := lbase + A B := int(inst & 0x1ff) //GETB if LVIsFalse(reg.Get(lbase + B)) { - reg.Set(RA, LTrue) + // +inline-call reg.Set RA LTrue } else { - reg.Set(RA, LFalse) + // +inline-call reg.Set RA LFalse } return 0 }, @@ -404,7 +414,7 @@ func init() { B := int(inst & 0x1ff) //GETB switch lv := L.rkValue(B).(type) { case LString: - reg.SetNumber(RA, LNumber(len(lv))) + // +inline-call reg.SetNumber RA LNumber(len(lv)) default: op := L.metaOp1(lv, "__len") if op.Type() == LTFunction { @@ -413,12 +423,13 @@ func init() { L.Call(1, 1) ret := reg.Pop() if ret.Type() == LTNumber { - reg.SetNumber(RA, ret.(LNumber)) + v, _ := ret.(LNumber) + // +inline-call reg.SetNumber RA v } else { - reg.Set(RA, ret) + // +inline-call reg.Set RA ret } } else if lv.Type() == LTTable { - reg.SetNumber(RA, LNumber(lv.(*LTable).Len())) + // +inline-call reg.SetNumber RA LNumber(lv.(*LTable).Len()) } else { L.RaiseError("__len undefined") } @@ -435,7 +446,8 @@ func init() { C := int(inst>>9) & 0x1ff //GETC RC := lbase + C RB := lbase + B - reg.Set(RA, stringConcat(L, RC-RB+1, RC)) + v := stringConcat(L, RC-RB+1, RC) + // +inline-call reg.Set RA v return 0 }, func(L *LState, inst uint32, baseframe *callFrame) int { //OP_JMP @@ -483,8 +495,8 @@ func init() { rhs := L.rkValue(C) ret := false - if v1, ok1 := lhs.assertFloat64(); ok1 { - if v2, ok2 := rhs.assertFloat64(); ok2 { + if v1, ok1 := lhs.(LNumber); ok1 { + if v2, ok2 := rhs.(LNumber); ok2 { ret = v1 <= v2 } else { L.RaiseError("attempt to compare %v with %v", lhs.Type().String(), rhs.Type().String()) @@ -538,7 +550,7 @@ func init() { B := int(inst & 0x1ff) //GETB C := int(inst>>9) & 0x1ff //GETC if value := reg.Get(lbase + B); LVAsBool(value) != (C == 0) { - reg.Set(RA, value) + // +inline-call reg.Set RA value } else { cf.Pc++ } @@ -560,7 +572,7 @@ func init() { nret := C - 1 var callable *LFunction var meta bool - if fn, ok := lv.assertFunction(); ok { + if fn, ok := lv.(*LFunction); ok { callable = fn meta = false } else { @@ -586,7 +598,7 @@ func init() { lv := reg.Get(RA) var callable *LFunction var meta bool - if fn, ok := lv.assertFunction(); ok { + if fn, ok := lv.(*LFunction); ok { callable = fn meta = false } else { @@ -673,17 +685,18 @@ func init() { lbase := cf.LocalBase A := int(inst>>18) & 0xff //GETA RA := lbase + A - if init, ok1 := reg.Get(RA).assertFloat64(); ok1 { - if limit, ok2 := reg.Get(RA + 1).assertFloat64(); ok2 { - if step, ok3 := reg.Get(RA + 2).assertFloat64(); ok3 { + if init, ok1 := reg.Get(RA).(LNumber); ok1 { + if limit, ok2 := reg.Get(RA + 1).(LNumber); ok2 { + if step, ok3 := reg.Get(RA + 2).(LNumber); ok3 { init += step - reg.SetNumber(RA, LNumber(init)) + v := LNumber(init) + // +inline-call reg.SetNumber RA v if (step > 0 && init <= limit) || (step <= 0 && init >= limit) { Sbx := int(inst&0x3ffff) - opMaxArgSbx //GETSBX cf.Pc += Sbx - reg.SetNumber(RA+3, LNumber(init)) + // +inline-call reg.SetNumber RA+3 v } else { - reg.SetTop(RA + 1) + // +inline-call reg.SetTop RA+1 } } else { L.RaiseError("for statement step must be a number") @@ -703,9 +716,9 @@ func init() { A := int(inst>>18) & 0xff //GETA RA := lbase + A Sbx := int(inst&0x3ffff) - opMaxArgSbx //GETSBX - if init, ok1 := reg.Get(RA).assertFloat64(); ok1 { - if step, ok2 := reg.Get(RA + 2).assertFloat64(); ok2 { - reg.SetNumber(RA, LNumber(init-step)) + if init, ok1 := reg.Get(RA).(LNumber); ok1 { + if step, ok2 := reg.Get(RA + 2).(LNumber); ok2 { + // +inline-call reg.SetNumber RA LNumber(init-step) } else { L.RaiseError("for statement step must be a number") } @@ -723,13 +736,13 @@ func init() { RA := lbase + A C := int(inst>>9) & 0x1ff //GETC nret := C - reg.SetTop(RA + 3 + 2) - reg.Set(RA+3+2, reg.Get(RA+2)) - reg.Set(RA+3+1, reg.Get(RA+1)) - reg.Set(RA+3, reg.Get(RA)) + // +inline-call reg.SetTop RA+3+2 + // +inline-call reg.Set RA+3+2 reg.Get(RA+2) + // +inline-call reg.Set RA+3+1 reg.Get(RA+1) + // +inline-call reg.Set RA+3 reg.Get(RA) L.callR(2, nret, RA+3) if value := reg.Get(RA + 3); value != LNil { - reg.Set(RA+2, value) + // +inline-call reg.Set RA+2 value pc := cf.Fn.Proto.Code[cf.Pc] cf.Pc += int(pc&0x3ffff) - opMaxArgSbx } @@ -776,7 +789,7 @@ func init() { Bx := int(inst & 0x3ffff) //GETBX proto := cf.Fn.Proto.FunctionPrototypes[Bx] closure := newLFunctionL(proto, cf.Fn.Env, int(proto.NumUpvalues)) - reg.Set(RA, closure) + // +inline-call reg.Set RA closure for i := 0; i < int(proto.NumUpvalues); i++ { inst = cf.Fn.Proto.Code[cf.Pc] cf.Pc++ @@ -826,12 +839,14 @@ func opArith(L *LState, inst uint32, baseframe *callFrame) int { //OP_ADD, OP_SU C := int(inst>>9) & 0x1ff //GETC lhs := L.rkValue(B) rhs := L.rkValue(C) - v1, ok1 := lhs.assertFloat64() - v2, ok2 := rhs.assertFloat64() + v1, ok1 := lhs.(LNumber) + v2, ok2 := rhs.(LNumber) if ok1 && ok2 { - reg.SetNumber(RA, numberArith(L, opcode, LNumber(v1), LNumber(v2))) + v := numberArith(L, opcode, LNumber(v1), LNumber(v2)) + // +inline-call reg.SetNumber RA v } else { - reg.Set(RA, objectArith(L, opcode, lhs, rhs)) + v := objectArith(L, opcode, lhs, rhs) + // +inline-call reg.Set RA v } return 0 } @@ -884,7 +899,7 @@ func objectArith(L *LState, opcode int, lhs, rhs LValue) LValue { event = "__pow" } op := L.metaOp2(lhs, rhs, event) - if op.Type() == LTFunction { + if _, ok := op.(*LFunction); ok { L.reg.Push(op) L.reg.Push(lhs) L.reg.Push(rhs) @@ -901,8 +916,8 @@ func objectArith(L *LState, opcode int, lhs, rhs LValue) LValue { rhs = rnum } } - if v1, ok1 := lhs.assertFloat64(); ok1 { - if v2, ok2 := rhs.assertFloat64(); ok2 { + if v1, ok1 := lhs.(LNumber); ok1 { + if v2, ok2 := rhs.(LNumber); ok2 { return numberArith(L, opcode, LNumber(v1), LNumber(v2)) } } @@ -951,8 +966,8 @@ func stringConcat(L *LState, total, last int) LValue { func lessThan(L *LState, lhs, rhs LValue) bool { // optimization for numbers - if v1, ok1 := lhs.assertFloat64(); ok1 { - if v2, ok2 := rhs.assertFloat64(); ok2 { + if v1, ok1 := lhs.(LNumber); ok1 { + if v2, ok2 := rhs.(LNumber); ok2 { return v1 < v2 } L.RaiseError("attempt to compare %v with %v", lhs.Type().String(), rhs.Type().String()) @@ -972,17 +987,18 @@ func lessThan(L *LState, lhs, rhs LValue) bool { } func equals(L *LState, lhs, rhs LValue, raw bool) bool { - if lhs.Type() != rhs.Type() { + lt := lhs.Type() + if lt != rhs.Type() { return false } ret := false - switch lhs.Type() { + switch lt { case LTNil: ret = true case LTNumber: - v1, _ := lhs.assertFloat64() - v2, _ := rhs.assertFloat64() + v1, _ := lhs.(LNumber) + v2, _ := rhs.(LNumber) ret = v1 == v2 case LTBool: ret = bool(lhs.(LBool)) == bool(rhs.(LBool)) diff --git a/vendor/github.com/yuin/gopher-lua/auxlib.go b/vendor/github.com/yuin/gopher-lua/auxlib.go index 61a3b8b6100a5..a022bdd89edc1 100644 --- a/vendor/github.com/yuin/gopher-lua/auxlib.go +++ b/vendor/github.com/yuin/gopher-lua/auxlib.go @@ -40,6 +40,11 @@ func (ls *LState) CheckNumber(n int) LNumber { if lv, ok := v.(LNumber); ok { return lv } + if lv, ok := v.(LString); ok { + if num, err := parseNumber(string(lv)); err == nil { + return num + } + } ls.TypeError(n, LTNumber) return 0 } @@ -413,7 +418,7 @@ func (ls *LState) DoString(source string) error { // ToStringMeta returns string representation of given LValue. // This method calls the `__tostring` meta method if defined. func (ls *LState) ToStringMeta(lv LValue) LValue { - if fn, ok := ls.metaOp1(lv, "__tostring").assertFunction(); ok { + if fn, ok := ls.metaOp1(lv, "__tostring").(*LFunction); ok { ls.Push(fn) ls.Push(lv) ls.Call(1, 1) diff --git a/vendor/github.com/yuin/gopher-lua/debuglib.go b/vendor/github.com/yuin/gopher-lua/debuglib.go index 41f883f1d067c..da8f5254b06eb 100644 --- a/vendor/github.com/yuin/gopher-lua/debuglib.go +++ b/vendor/github.com/yuin/gopher-lua/debuglib.go @@ -155,8 +155,8 @@ func debugTraceback(L *LState) int { level := L.OptInt(2, 1) ls := L if L.GetTop() > 0 { - if s, ok := L.Get(1).assertString(); ok { - msg = s + if s, ok := L.Get(1).(LString); ok { + msg = string(s) } if l, ok := L.Get(1).(*LState); ok { ls = l diff --git a/vendor/github.com/yuin/gopher-lua/iolib.go b/vendor/github.com/yuin/gopher-lua/iolib.go index 4a86f89362d07..3f5f295cedc35 100644 --- a/vendor/github.com/yuin/gopher-lua/iolib.go +++ b/vendor/github.com/yuin/gopher-lua/iolib.go @@ -629,7 +629,7 @@ func ioOpenFile(L *LState) int { mode = os.O_RDONLY writable = false case "w", "wb": - mode = os.O_WRONLY | os.O_CREATE + mode = os.O_WRONLY | os.O_TRUNC | os.O_CREATE readable = false case "a", "ab": mode = os.O_WRONLY | os.O_APPEND | os.O_CREATE @@ -658,6 +658,9 @@ func ioPopen(L *LState) int { cmd := L.CheckString(1) if L.GetTop() == 1 { L.Push(LString("r")) + } else if L.GetTop() > 1 && (L.Get(2)).Type() == LTNil { + L.SetTop(1) + L.Push(LString("r")) } var file *LUserData var err error diff --git a/vendor/github.com/yuin/gopher-lua/oslib.go b/vendor/github.com/yuin/gopher-lua/oslib.go index 256c88113003b..5448cc1f8b2c3 100644 --- a/vendor/github.com/yuin/gopher-lua/oslib.go +++ b/vendor/github.com/yuin/gopher-lua/oslib.go @@ -23,7 +23,7 @@ func getIntField(L *LState, tb *LTable, key string, v int) int { slv := string(lv) slv = strings.TrimLeft(slv, " ") if strings.HasPrefix(slv, "0") && !strings.HasPrefix(slv, "0x") && !strings.HasPrefix(slv, "0X") { - //Standard lua interpreter only support decimal and hexadecimal + // Standard lua interpreter only support decimal and hexadecimal slv = strings.TrimLeft(slv, "0") if slv == "" { return 0 @@ -106,16 +106,20 @@ func osExit(L *LState) int { func osDate(L *LState) int { t := time.Now() + isUTC := false cfmt := "%c" if L.GetTop() >= 1 { cfmt = L.CheckString(1) if strings.HasPrefix(cfmt, "!") { - t = time.Now().UTC() cfmt = strings.TrimLeft(cfmt, "!") + isUTC = true } if L.GetTop() >= 2 { t = time.Unix(L.CheckInt64(2), 0) } + if isUTC { + t = t.UTC() + } if strings.HasPrefix(cfmt, "*t") { ret := L.NewTable() ret.RawSetString("year", LNumber(t.Year())) diff --git a/vendor/github.com/yuin/gopher-lua/state.go b/vendor/github.com/yuin/gopher-lua/state.go index a1ee672e9cbd3..292f93b48ad83 100644 --- a/vendor/github.com/yuin/gopher-lua/state.go +++ b/vendor/github.com/yuin/gopher-lua/state.go @@ -402,24 +402,25 @@ func (rg *registry) forceResize(newSize int) { copy(newSlice, rg.array[:rg.top]) // should we copy the area beyond top? there shouldn't be any valid values there so it shouldn't be necessary. rg.array = newSlice } -func (rg *registry) SetTop(top int) { + +func (rg *registry) SetTop(topi int) { // +inline-start // this section is inlined by go-inline // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' { - requiredSize := top + requiredSize := topi if requiredSize > cap(rg.array) { rg.resize(requiredSize) } } - oldtop := rg.top - rg.top = top - for i := oldtop; i < rg.top; i++ { + oldtopi := rg.top + rg.top = topi + for i := oldtopi; i < rg.top; i++ { rg.array[i] = LNil } // values beyond top don't need to be valid LValues, so setting them to nil is fine // setting them to nil rather than LNil lets us invoke the golang memclr opto - if rg.top < oldtop { - nilRange := rg.array[rg.top:oldtop] + if rg.top < oldtopi { + nilRange := rg.array[rg.top:oldtopi] for i := range nilRange { nilRange[i] = nil } @@ -427,7 +428,7 @@ func (rg *registry) SetTop(top int) { //for i := rg.top; i < oldtop; i++ { // rg.array[i] = LNil //} -} +} // +inline-end func (rg *registry) Top() int { return rg.top @@ -530,19 +531,73 @@ func (rg *registry) FillNil(regm, n int) { // +inline-start func (rg *registry) Insert(value LValue, reg int) { top := rg.Top() if reg >= top { - rg.Set(reg, value) + // this section is inlined by go-inline + // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' + { + regi := reg + vali := value + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = vali + if regi >= rg.top { + rg.top = regi + 1 + } + } return } top-- for ; top >= reg; top-- { // FIXME consider using copy() here if Insert() is called enough - rg.Set(top+1, rg.Get(top)) + // this section is inlined by go-inline + // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' + { + regi := top + 1 + vali := rg.Get(top) + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = vali + if regi >= rg.top { + rg.top = regi + 1 + } + } + } + // this section is inlined by go-inline + // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' + { + regi := reg + vali := value + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = vali + if regi >= rg.top { + rg.top = regi + 1 + } } - rg.Set(reg, value) } -func (rg *registry) Set(reg int, val LValue) { - newSize := reg + 1 +func (rg *registry) Set(regi int, vali LValue) { // +inline-start + newSize := regi + 1 // this section is inlined by go-inline // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' { @@ -551,14 +606,14 @@ func (rg *registry) Set(reg int, val LValue) { rg.resize(requiredSize) } } - rg.array[reg] = val - if reg >= rg.top { - rg.top = reg + 1 + rg.array[regi] = vali + if regi >= rg.top { + rg.top = regi + 1 } -} +} // +inline-end -func (rg *registry) SetNumber(reg int, val LNumber) { - newSize := reg + 1 +func (rg *registry) SetNumber(regi int, vali LNumber) { // +inline-start + newSize := regi + 1 // this section is inlined by go-inline // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' { @@ -567,11 +622,11 @@ func (rg *registry) SetNumber(reg int, val LNumber) { rg.resize(requiredSize) } } - rg.array[reg] = rg.alloc.LNumber2I(val) - if reg >= rg.top { - rg.top = reg + 1 + rg.array[regi] = rg.alloc.LNumber2I(vali) + if regi >= rg.top { + rg.top = regi + 1 } -} +} // +inline-end func (rg *registry) IsFull() bool { return rg.top >= cap(rg.array) @@ -815,6 +870,9 @@ func (ls *LState) isStarted() bool { func (ls *LState) kill() { ls.Dead = true + if ls.ctxCancelFn != nil { + ls.ctxCancelFn() + } } func (ls *LState) indexToReg(idx int) int { @@ -1561,6 +1619,7 @@ func (ls *LState) NewThread() (*LState, context.CancelFunc) { if ls.ctx != nil { thread.mainLoop = mainLoopWithContext thread.ctx, f = context.WithCancel(ls.ctx) + thread.ctxCancelFn = f } return thread, f } @@ -2010,6 +2069,9 @@ func (ls *LState) PCall(nargs, nret int, errfunc *LFunction) (err error) { err = rcv.(*ApiError) err.(*ApiError).StackTrace = ls.stackTrace(0) } + ls.stack.SetSp(sp) + ls.currentFrame = ls.stack.Last() + ls.reg.SetTop(base) } }() ls.Call(1, 1) diff --git a/vendor/github.com/yuin/gopher-lua/value.go b/vendor/github.com/yuin/gopher-lua/value.go index 0d4af80816ec9..4156e9d5b26ea 100644 --- a/vendor/github.com/yuin/gopher-lua/value.go +++ b/vendor/github.com/yuin/gopher-lua/value.go @@ -29,12 +29,6 @@ func (vt LValueType) String() string { type LValue interface { String() string Type() LValueType - // to reduce `runtime.assertI2T2` costs, this method should be used instead of the type assertion in heavy paths(typically inside the VM). - assertFloat64() (float64, bool) - // to reduce `runtime.assertI2T2` costs, this method should be used instead of the type assertion in heavy paths(typically inside the VM). - assertString() (string, bool) - // to reduce `runtime.assertI2T2` costs, this method should be used instead of the type assertion in heavy paths(typically inside the VM). - assertFunction() (*LFunction, bool) } // LVIsFalse returns true if a given LValue is a nil or false otherwise false. @@ -80,11 +74,8 @@ func LVAsNumber(v LValue) LNumber { type LNilType struct{} -func (nl *LNilType) String() string { return "nil" } -func (nl *LNilType) Type() LValueType { return LTNil } -func (nl *LNilType) assertFloat64() (float64, bool) { return 0, false } -func (nl *LNilType) assertString() (string, bool) { return "", false } -func (nl *LNilType) assertFunction() (*LFunction, bool) { return nil, false } +func (nl *LNilType) String() string { return "nil" } +func (nl *LNilType) Type() LValueType { return LTNil } var LNil = LValue(&LNilType{}) @@ -96,21 +87,15 @@ func (bl LBool) String() string { } return "false" } -func (bl LBool) Type() LValueType { return LTBool } -func (bl LBool) assertFloat64() (float64, bool) { return 0, false } -func (bl LBool) assertString() (string, bool) { return "", false } -func (bl LBool) assertFunction() (*LFunction, bool) { return nil, false } +func (bl LBool) Type() LValueType { return LTBool } var LTrue = LBool(true) var LFalse = LBool(false) type LString string -func (st LString) String() string { return string(st) } -func (st LString) Type() LValueType { return LTString } -func (st LString) assertFloat64() (float64, bool) { return 0, false } -func (st LString) assertString() (string, bool) { return string(st), true } -func (st LString) assertFunction() (*LFunction, bool) { return nil, false } +func (st LString) String() string { return string(st) } +func (st LString) Type() LValueType { return LTString } // fmt.Formatter interface func (st LString) Format(f fmt.State, c rune) { @@ -133,10 +118,7 @@ func (nm LNumber) String() string { return fmt.Sprint(float64(nm)) } -func (nm LNumber) Type() LValueType { return LTNumber } -func (nm LNumber) assertFloat64() (float64, bool) { return float64(nm), true } -func (nm LNumber) assertString() (string, bool) { return "", false } -func (nm LNumber) assertFunction() (*LFunction, bool) { return nil, false } +func (nm LNumber) Type() LValueType { return LTNumber } // fmt.Formatter interface func (nm LNumber) Format(f fmt.State, c rune) { @@ -168,11 +150,8 @@ type LTable struct { k2i map[LValue]int } -func (tb *LTable) String() string { return fmt.Sprintf("table: %p", tb) } -func (tb *LTable) Type() LValueType { return LTTable } -func (tb *LTable) assertFloat64() (float64, bool) { return 0, false } -func (tb *LTable) assertString() (string, bool) { return "", false } -func (tb *LTable) assertFunction() (*LFunction, bool) { return nil, false } +func (tb *LTable) String() string { return fmt.Sprintf("table: %p", tb) } +func (tb *LTable) Type() LValueType { return LTTable } type LFunction struct { IsG bool @@ -183,11 +162,8 @@ type LFunction struct { } type LGFunction func(*LState) int -func (fn *LFunction) String() string { return fmt.Sprintf("function: %p", fn) } -func (fn *LFunction) Type() LValueType { return LTFunction } -func (fn *LFunction) assertFloat64() (float64, bool) { return 0, false } -func (fn *LFunction) assertString() (string, bool) { return "", false } -func (fn *LFunction) assertFunction() (*LFunction, bool) { return fn, true } +func (fn *LFunction) String() string { return fmt.Sprintf("function: %p", fn) } +func (fn *LFunction) Type() LValueType { return LTFunction } type Global struct { MainThread *LState @@ -218,13 +194,11 @@ type LState struct { hasErrorFunc bool mainLoop func(*LState, *callFrame) ctx context.Context + ctxCancelFn context.CancelFunc } -func (ls *LState) String() string { return fmt.Sprintf("thread: %p", ls) } -func (ls *LState) Type() LValueType { return LTThread } -func (ls *LState) assertFloat64() (float64, bool) { return 0, false } -func (ls *LState) assertString() (string, bool) { return "", false } -func (ls *LState) assertFunction() (*LFunction, bool) { return nil, false } +func (ls *LState) String() string { return fmt.Sprintf("thread: %p", ls) } +func (ls *LState) Type() LValueType { return LTThread } type LUserData struct { Value interface{} @@ -232,16 +206,10 @@ type LUserData struct { Metatable LValue } -func (ud *LUserData) String() string { return fmt.Sprintf("userdata: %p", ud) } -func (ud *LUserData) Type() LValueType { return LTUserData } -func (ud *LUserData) assertFloat64() (float64, bool) { return 0, false } -func (ud *LUserData) assertString() (string, bool) { return "", false } -func (ud *LUserData) assertFunction() (*LFunction, bool) { return nil, false } +func (ud *LUserData) String() string { return fmt.Sprintf("userdata: %p", ud) } +func (ud *LUserData) Type() LValueType { return LTUserData } type LChannel chan LValue -func (ch LChannel) String() string { return fmt.Sprintf("channel: %p", ch) } -func (ch LChannel) Type() LValueType { return LTChannel } -func (ch LChannel) assertFloat64() (float64, bool) { return 0, false } -func (ch LChannel) assertString() (string, bool) { return "", false } -func (ch LChannel) assertFunction() (*LFunction, bool) { return nil, false } +func (ch LChannel) String() string { return fmt.Sprintf("channel: %p", ch) } +func (ch LChannel) Type() LValueType { return LTChannel } diff --git a/vendor/github.com/yuin/gopher-lua/vm.go b/vendor/github.com/yuin/gopher-lua/vm.go index 470855f2af65f..97335a75098f3 100644 --- a/vendor/github.com/yuin/gopher-lua/vm.go +++ b/vendor/github.com/yuin/gopher-lua/vm.go @@ -307,7 +307,27 @@ func init() { A := int(inst>>18) & 0xff //GETA RA := lbase + A B := int(inst & 0x1ff) //GETB - reg.Set(RA, reg.Get(lbase+B)) + v := reg.Get(lbase + B) + // this section is inlined by go-inline + // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' + { + rg := reg + regi := RA + vali := v + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = vali + if regi >= rg.top { + rg.top = regi + 1 + } + } return 0 }, func(L *LState, inst uint32, baseframe *callFrame) int { //OP_MOVEN @@ -317,7 +337,27 @@ func init() { A := int(inst>>18) & 0xff //GETA B := int(inst & 0x1ff) //GETB C := int(inst>>9) & 0x1ff //GETC - reg.Set(lbase+A, reg.Get(lbase+B)) + v := reg.Get(lbase + B) + // this section is inlined by go-inline + // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' + { + rg := reg + regi := lbase + A + vali := v + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = vali + if regi >= rg.top { + rg.top = regi + 1 + } + } code := cf.Fn.Proto.Code pc := cf.Pc for i := 0; i < C; i++ { @@ -325,7 +365,27 @@ func init() { pc++ A = int(inst>>18) & 0xff //GETA B = int(inst & 0x1ff) //GETB - reg.Set(lbase+A, reg.Get(lbase+B)) + v := reg.Get(lbase + B) + // this section is inlined by go-inline + // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' + { + rg := reg + regi := lbase + A + vali := v + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = vali + if regi >= rg.top { + rg.top = regi + 1 + } + } } cf.Pc = pc return 0 @@ -337,7 +397,27 @@ func init() { A := int(inst>>18) & 0xff //GETA RA := lbase + A Bx := int(inst & 0x3ffff) //GETBX - reg.Set(RA, cf.Fn.Proto.Constants[Bx]) + v := cf.Fn.Proto.Constants[Bx] + // this section is inlined by go-inline + // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' + { + rg := reg + regi := RA + vali := v + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = vali + if regi >= rg.top { + rg.top = regi + 1 + } + } return 0 }, func(L *LState, inst uint32, baseframe *callFrame) int { //OP_LOADBOOL @@ -349,9 +429,47 @@ func init() { B := int(inst & 0x1ff) //GETB C := int(inst>>9) & 0x1ff //GETC if B != 0 { - reg.Set(RA, LTrue) + // this section is inlined by go-inline + // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' + { + rg := reg + regi := RA + vali := LTrue + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = vali + if regi >= rg.top { + rg.top = regi + 1 + } + } } else { - reg.Set(RA, LFalse) + // this section is inlined by go-inline + // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' + { + rg := reg + regi := RA + vali := LFalse + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = vali + if regi >= rg.top { + rg.top = regi + 1 + } + } } if C != 0 { cf.Pc++ @@ -366,7 +484,26 @@ func init() { RA := lbase + A B := int(inst & 0x1ff) //GETB for i := RA; i <= lbase+B; i++ { - reg.Set(i, LNil) + // this section is inlined by go-inline + // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' + { + rg := reg + regi := i + vali := LNil + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = vali + if regi >= rg.top { + rg.top = regi + 1 + } + } } return 0 }, @@ -377,7 +514,27 @@ func init() { A := int(inst>>18) & 0xff //GETA RA := lbase + A B := int(inst & 0x1ff) //GETB - reg.Set(RA, cf.Fn.Upvalues[B].Value()) + v := cf.Fn.Upvalues[B].Value() + // this section is inlined by go-inline + // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' + { + rg := reg + regi := RA + vali := v + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = vali + if regi >= rg.top { + rg.top = regi + 1 + } + } return 0 }, func(L *LState, inst uint32, baseframe *callFrame) int { //OP_GETGLOBAL @@ -388,7 +545,27 @@ func init() { RA := lbase + A Bx := int(inst & 0x3ffff) //GETBX //reg.Set(RA, L.getField(cf.Fn.Env, cf.Fn.Proto.Constants[Bx])) - reg.Set(RA, L.getFieldString(cf.Fn.Env, cf.Fn.Proto.stringConstants[Bx])) + v := L.getFieldString(cf.Fn.Env, cf.Fn.Proto.stringConstants[Bx]) + // this section is inlined by go-inline + // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' + { + rg := reg + regi := RA + vali := v + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = vali + if regi >= rg.top { + rg.top = regi + 1 + } + } return 0 }, func(L *LState, inst uint32, baseframe *callFrame) int { //OP_GETTABLE @@ -399,7 +576,27 @@ func init() { RA := lbase + A B := int(inst & 0x1ff) //GETB C := int(inst>>9) & 0x1ff //GETC - reg.Set(RA, L.getField(reg.Get(lbase+B), L.rkValue(C))) + v := L.getField(reg.Get(lbase+B), L.rkValue(C)) + // this section is inlined by go-inline + // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' + { + rg := reg + regi := RA + vali := v + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = vali + if regi >= rg.top { + rg.top = regi + 1 + } + } return 0 }, func(L *LState, inst uint32, baseframe *callFrame) int { //OP_GETTABLEKS @@ -410,7 +607,27 @@ func init() { RA := lbase + A B := int(inst & 0x1ff) //GETB C := int(inst>>9) & 0x1ff //GETC - reg.Set(RA, L.getFieldString(reg.Get(lbase+B), L.rkString(C))) + v := L.getFieldString(reg.Get(lbase+B), L.rkString(C)) + // this section is inlined by go-inline + // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' + { + rg := reg + regi := RA + vali := v + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = vali + if regi >= rg.top { + rg.top = regi + 1 + } + } return 0 }, func(L *LState, inst uint32, baseframe *callFrame) int { //OP_SETGLOBAL @@ -464,7 +681,27 @@ func init() { RA := lbase + A B := int(inst & 0x1ff) //GETB C := int(inst>>9) & 0x1ff //GETC - reg.Set(RA, newLTable(B, C)) + v := newLTable(B, C) + // this section is inlined by go-inline + // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' + { + rg := reg + regi := RA + vali := v + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = vali + if regi >= rg.top { + rg.top = regi + 1 + } + } return 0 }, func(L *LState, inst uint32, baseframe *callFrame) int { //OP_SELF @@ -476,8 +713,47 @@ func init() { B := int(inst & 0x1ff) //GETB C := int(inst>>9) & 0x1ff //GETC selfobj := reg.Get(lbase + B) - reg.Set(RA, L.getFieldString(selfobj, L.rkString(C))) - reg.Set(RA+1, selfobj) + v := L.getFieldString(selfobj, L.rkString(C)) + // this section is inlined by go-inline + // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' + { + rg := reg + regi := RA + vali := v + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = vali + if regi >= rg.top { + rg.top = regi + 1 + } + } + // this section is inlined by go-inline + // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' + { + rg := reg + regi := RA + 1 + vali := selfobj + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = vali + if regi >= rg.top { + rg.top = regi + 1 + } + } return 0 }, opArith, // OP_ADD @@ -495,17 +771,74 @@ func init() { B := int(inst & 0x1ff) //GETB unaryv := L.rkValue(B) if nm, ok := unaryv.(LNumber); ok { - reg.SetNumber(RA, -nm) + // this section is inlined by go-inline + // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' + { + rg := reg + regi := RA + vali := -nm + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = vali + if regi >= rg.top { + rg.top = regi + 1 + } + } } else { op := L.metaOp1(unaryv, "__unm") if op.Type() == LTFunction { reg.Push(op) reg.Push(unaryv) L.Call(1, 1) - reg.Set(RA, reg.Pop()) + // this section is inlined by go-inline + // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' + { + rg := reg + regi := RA + vali := reg.Pop() + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = vali + if regi >= rg.top { + rg.top = regi + 1 + } + } } else if str, ok1 := unaryv.(LString); ok1 { if num, err := parseNumber(string(str)); err == nil { - reg.Set(RA, -num) + // this section is inlined by go-inline + // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' + { + rg := reg + regi := RA + vali := -num + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = vali + if regi >= rg.top { + rg.top = regi + 1 + } + } } else { L.RaiseError("__unm undefined") } @@ -523,9 +856,47 @@ func init() { RA := lbase + A B := int(inst & 0x1ff) //GETB if LVIsFalse(reg.Get(lbase + B)) { - reg.Set(RA, LTrue) + // this section is inlined by go-inline + // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' + { + rg := reg + regi := RA + vali := LTrue + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = vali + if regi >= rg.top { + rg.top = regi + 1 + } + } } else { - reg.Set(RA, LFalse) + // this section is inlined by go-inline + // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' + { + rg := reg + regi := RA + vali := LFalse + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = vali + if regi >= rg.top { + rg.top = regi + 1 + } + } } return 0 }, @@ -538,7 +909,26 @@ func init() { B := int(inst & 0x1ff) //GETB switch lv := L.rkValue(B).(type) { case LString: - reg.SetNumber(RA, LNumber(len(lv))) + // this section is inlined by go-inline + // source function is 'func (rg *registry) SetNumber(regi int, vali LNumber) ' in '_state.go' + { + rg := reg + regi := RA + vali := LNumber(len(lv)) + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = rg.alloc.LNumber2I(vali) + if regi >= rg.top { + rg.top = regi + 1 + } + } default: op := L.metaOp1(lv, "__len") if op.Type() == LTFunction { @@ -547,12 +937,70 @@ func init() { L.Call(1, 1) ret := reg.Pop() if ret.Type() == LTNumber { - reg.SetNumber(RA, ret.(LNumber)) + v, _ := ret.(LNumber) + // this section is inlined by go-inline + // source function is 'func (rg *registry) SetNumber(regi int, vali LNumber) ' in '_state.go' + { + rg := reg + regi := RA + vali := v + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = rg.alloc.LNumber2I(vali) + if regi >= rg.top { + rg.top = regi + 1 + } + } } else { - reg.Set(RA, ret) + // this section is inlined by go-inline + // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' + { + rg := reg + regi := RA + vali := ret + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = vali + if regi >= rg.top { + rg.top = regi + 1 + } + } } } else if lv.Type() == LTTable { - reg.SetNumber(RA, LNumber(lv.(*LTable).Len())) + // this section is inlined by go-inline + // source function is 'func (rg *registry) SetNumber(regi int, vali LNumber) ' in '_state.go' + { + rg := reg + regi := RA + vali := LNumber(lv.(*LTable).Len()) + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = rg.alloc.LNumber2I(vali) + if regi >= rg.top { + rg.top = regi + 1 + } + } } else { L.RaiseError("__len undefined") } @@ -569,7 +1017,27 @@ func init() { C := int(inst>>9) & 0x1ff //GETC RC := lbase + C RB := lbase + B - reg.Set(RA, stringConcat(L, RC-RB+1, RC)) + v := stringConcat(L, RC-RB+1, RC) + // this section is inlined by go-inline + // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' + { + rg := reg + regi := RA + vali := v + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = vali + if regi >= rg.top { + rg.top = regi + 1 + } + } return 0 }, func(L *LState, inst uint32, baseframe *callFrame) int { //OP_JMP @@ -617,8 +1085,8 @@ func init() { rhs := L.rkValue(C) ret := false - if v1, ok1 := lhs.assertFloat64(); ok1 { - if v2, ok2 := rhs.assertFloat64(); ok2 { + if v1, ok1 := lhs.(LNumber); ok1 { + if v2, ok2 := rhs.(LNumber); ok2 { ret = v1 <= v2 } else { L.RaiseError("attempt to compare %v with %v", lhs.Type().String(), rhs.Type().String()) @@ -672,7 +1140,26 @@ func init() { B := int(inst & 0x1ff) //GETB C := int(inst>>9) & 0x1ff //GETC if value := reg.Get(lbase + B); LVAsBool(value) != (C == 0) { - reg.Set(RA, value) + // this section is inlined by go-inline + // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' + { + rg := reg + regi := RA + vali := value + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = vali + if regi >= rg.top { + rg.top = regi + 1 + } + } } else { cf.Pc++ } @@ -694,7 +1181,7 @@ func init() { nret := C - 1 var callable *LFunction var meta bool - if fn, ok := lv.assertFunction(); ok { + if fn, ok := lv.(*LFunction); ok { callable = fn meta = false } else { @@ -837,7 +1324,7 @@ func init() { lv := reg.Get(RA) var callable *LFunction var meta bool - if fn, ok := lv.assertFunction(); ok { + if fn, ok := lv.(*LFunction); ok { callable = fn meta = false } else { @@ -1308,17 +1795,85 @@ func init() { lbase := cf.LocalBase A := int(inst>>18) & 0xff //GETA RA := lbase + A - if init, ok1 := reg.Get(RA).assertFloat64(); ok1 { - if limit, ok2 := reg.Get(RA + 1).assertFloat64(); ok2 { - if step, ok3 := reg.Get(RA + 2).assertFloat64(); ok3 { + if init, ok1 := reg.Get(RA).(LNumber); ok1 { + if limit, ok2 := reg.Get(RA + 1).(LNumber); ok2 { + if step, ok3 := reg.Get(RA + 2).(LNumber); ok3 { init += step - reg.SetNumber(RA, LNumber(init)) + v := LNumber(init) + // this section is inlined by go-inline + // source function is 'func (rg *registry) SetNumber(regi int, vali LNumber) ' in '_state.go' + { + rg := reg + regi := RA + vali := v + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = rg.alloc.LNumber2I(vali) + if regi >= rg.top { + rg.top = regi + 1 + } + } if (step > 0 && init <= limit) || (step <= 0 && init >= limit) { Sbx := int(inst&0x3ffff) - opMaxArgSbx //GETSBX cf.Pc += Sbx - reg.SetNumber(RA+3, LNumber(init)) + // this section is inlined by go-inline + // source function is 'func (rg *registry) SetNumber(regi int, vali LNumber) ' in '_state.go' + { + rg := reg + regi := RA + 3 + vali := v + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = rg.alloc.LNumber2I(vali) + if regi >= rg.top { + rg.top = regi + 1 + } + } } else { - reg.SetTop(RA + 1) + // this section is inlined by go-inline + // source function is 'func (rg *registry) SetTop(topi int) ' in '_state.go' + { + rg := reg + topi := RA + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := topi + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + oldtopi := rg.top + rg.top = topi + for i := oldtopi; i < rg.top; i++ { + rg.array[i] = LNil + } + // values beyond top don't need to be valid LValues, so setting them to nil is fine + // setting them to nil rather than LNil lets us invoke the golang memclr opto + if rg.top < oldtopi { + nilRange := rg.array[rg.top:oldtopi] + for i := range nilRange { + nilRange[i] = nil + } + } + //for i := rg.top; i < oldtop; i++ { + // rg.array[i] = LNil + //} + } } } else { L.RaiseError("for statement step must be a number") @@ -1338,9 +1893,28 @@ func init() { A := int(inst>>18) & 0xff //GETA RA := lbase + A Sbx := int(inst&0x3ffff) - opMaxArgSbx //GETSBX - if init, ok1 := reg.Get(RA).assertFloat64(); ok1 { - if step, ok2 := reg.Get(RA + 2).assertFloat64(); ok2 { - reg.SetNumber(RA, LNumber(init-step)) + if init, ok1 := reg.Get(RA).(LNumber); ok1 { + if step, ok2 := reg.Get(RA + 2).(LNumber); ok2 { + // this section is inlined by go-inline + // source function is 'func (rg *registry) SetNumber(regi int, vali LNumber) ' in '_state.go' + { + rg := reg + regi := RA + vali := LNumber(init - step) + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = rg.alloc.LNumber2I(vali) + if regi >= rg.top { + rg.top = regi + 1 + } + } } else { L.RaiseError("for statement step must be a number") } @@ -1358,13 +1932,118 @@ func init() { RA := lbase + A C := int(inst>>9) & 0x1ff //GETC nret := C - reg.SetTop(RA + 3 + 2) - reg.Set(RA+3+2, reg.Get(RA+2)) - reg.Set(RA+3+1, reg.Get(RA+1)) - reg.Set(RA+3, reg.Get(RA)) + // this section is inlined by go-inline + // source function is 'func (rg *registry) SetTop(topi int) ' in '_state.go' + { + rg := reg + topi := RA + 3 + 2 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := topi + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + oldtopi := rg.top + rg.top = topi + for i := oldtopi; i < rg.top; i++ { + rg.array[i] = LNil + } + // values beyond top don't need to be valid LValues, so setting them to nil is fine + // setting them to nil rather than LNil lets us invoke the golang memclr opto + if rg.top < oldtopi { + nilRange := rg.array[rg.top:oldtopi] + for i := range nilRange { + nilRange[i] = nil + } + } + //for i := rg.top; i < oldtop; i++ { + // rg.array[i] = LNil + //} + } + // this section is inlined by go-inline + // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' + { + rg := reg + regi := RA + 3 + 2 + vali := reg.Get(RA + 2) + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = vali + if regi >= rg.top { + rg.top = regi + 1 + } + } + // this section is inlined by go-inline + // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' + { + rg := reg + regi := RA + 3 + 1 + vali := reg.Get(RA + 1) + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = vali + if regi >= rg.top { + rg.top = regi + 1 + } + } + // this section is inlined by go-inline + // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' + { + rg := reg + regi := RA + 3 + vali := reg.Get(RA) + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = vali + if regi >= rg.top { + rg.top = regi + 1 + } + } L.callR(2, nret, RA+3) if value := reg.Get(RA + 3); value != LNil { - reg.Set(RA+2, value) + // this section is inlined by go-inline + // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' + { + rg := reg + regi := RA + 2 + vali := value + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = vali + if regi >= rg.top { + rg.top = regi + 1 + } + } pc := cf.Fn.Proto.Code[cf.Pc] cf.Pc += int(pc&0x3ffff) - opMaxArgSbx } @@ -1430,7 +2109,26 @@ func init() { Bx := int(inst & 0x3ffff) //GETBX proto := cf.Fn.Proto.FunctionPrototypes[Bx] closure := newLFunctionL(proto, cf.Fn.Env, int(proto.NumUpvalues)) - reg.Set(RA, closure) + // this section is inlined by go-inline + // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' + { + rg := reg + regi := RA + vali := closure + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = vali + if regi >= rg.top { + rg.top = regi + 1 + } + } for i := 0; i < int(proto.NumUpvalues); i++ { inst = cf.Fn.Proto.Code[cf.Pc] cf.Pc++ @@ -1519,12 +2217,52 @@ func opArith(L *LState, inst uint32, baseframe *callFrame) int { //OP_ADD, OP_SU C := int(inst>>9) & 0x1ff //GETC lhs := L.rkValue(B) rhs := L.rkValue(C) - v1, ok1 := lhs.assertFloat64() - v2, ok2 := rhs.assertFloat64() + v1, ok1 := lhs.(LNumber) + v2, ok2 := rhs.(LNumber) if ok1 && ok2 { - reg.SetNumber(RA, numberArith(L, opcode, LNumber(v1), LNumber(v2))) + v := numberArith(L, opcode, LNumber(v1), LNumber(v2)) + // this section is inlined by go-inline + // source function is 'func (rg *registry) SetNumber(regi int, vali LNumber) ' in '_state.go' + { + rg := reg + regi := RA + vali := v + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = rg.alloc.LNumber2I(vali) + if regi >= rg.top { + rg.top = regi + 1 + } + } } else { - reg.Set(RA, objectArith(L, opcode, lhs, rhs)) + v := objectArith(L, opcode, lhs, rhs) + // this section is inlined by go-inline + // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' + { + rg := reg + regi := RA + vali := v + newSize := regi + 1 + // this section is inlined by go-inline + // source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go' + { + requiredSize := newSize + if requiredSize > cap(rg.array) { + rg.resize(requiredSize) + } + } + rg.array[regi] = vali + if regi >= rg.top { + rg.top = regi + 1 + } + } } return 0 } @@ -1577,7 +2315,7 @@ func objectArith(L *LState, opcode int, lhs, rhs LValue) LValue { event = "__pow" } op := L.metaOp2(lhs, rhs, event) - if op.Type() == LTFunction { + if _, ok := op.(*LFunction); ok { L.reg.Push(op) L.reg.Push(lhs) L.reg.Push(rhs) @@ -1594,8 +2332,8 @@ func objectArith(L *LState, opcode int, lhs, rhs LValue) LValue { rhs = rnum } } - if v1, ok1 := lhs.assertFloat64(); ok1 { - if v2, ok2 := rhs.assertFloat64(); ok2 { + if v1, ok1 := lhs.(LNumber); ok1 { + if v2, ok2 := rhs.(LNumber); ok2 { return numberArith(L, opcode, LNumber(v1), LNumber(v2)) } } @@ -1644,8 +2382,8 @@ func stringConcat(L *LState, total, last int) LValue { func lessThan(L *LState, lhs, rhs LValue) bool { // optimization for numbers - if v1, ok1 := lhs.assertFloat64(); ok1 { - if v2, ok2 := rhs.assertFloat64(); ok2 { + if v1, ok1 := lhs.(LNumber); ok1 { + if v2, ok2 := rhs.(LNumber); ok2 { return v1 < v2 } L.RaiseError("attempt to compare %v with %v", lhs.Type().String(), rhs.Type().String()) @@ -1665,17 +2403,18 @@ func lessThan(L *LState, lhs, rhs LValue) bool { } func equals(L *LState, lhs, rhs LValue, raw bool) bool { - if lhs.Type() != rhs.Type() { + lt := lhs.Type() + if lt != rhs.Type() { return false } ret := false - switch lhs.Type() { + switch lt { case LTNil: ret = true case LTNumber: - v1, _ := lhs.assertFloat64() - v2, _ := rhs.assertFloat64() + v1, _ := lhs.(LNumber) + v2, _ := rhs.(LNumber) ret = v1 == v2 case LTBool: ret = bool(lhs.(LBool)) == bool(rhs.(LBool)) diff --git a/vendor/modules.txt b/vendor/modules.txt index 9b50cc3544026..66725a5eba42c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -333,13 +333,16 @@ 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.30.4 -## explicit; go 1.14 +# github.com/alicebob/miniredis/v2 v2.33.0 +## explicit; go 1.17 github.com/alicebob/miniredis/v2 +github.com/alicebob/miniredis/v2/fpconv github.com/alicebob/miniredis/v2/geohash github.com/alicebob/miniredis/v2/hyperloglog github.com/alicebob/miniredis/v2/metro +github.com/alicebob/miniredis/v2/proto github.com/alicebob/miniredis/v2/server +github.com/alicebob/miniredis/v2/size # github.com/aliyun/aliyun-oss-go-sdk v2.2.10+incompatible ## explicit github.com/aliyun/aliyun-oss-go-sdk/oss @@ -1682,7 +1685,7 @@ github.com/xdg-go/scram # github.com/xdg-go/stringprep v1.0.4 ## explicit; go 1.11 github.com/xdg-go/stringprep -# github.com/yuin/gopher-lua v1.1.0 +# github.com/yuin/gopher-lua v1.1.1 ## explicit; go 1.17 github.com/yuin/gopher-lua github.com/yuin/gopher-lua/ast