Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
xindoo
redis
提交
7f490b19
R
redis
项目概览
xindoo
/
redis
通知
2
Star
2
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
R
redis
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
7f490b19
编写于
7月 09, 2012
作者:
P
Pieter Noordhuis
提交者:
antirez
10月 25, 2013
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Add SCAN command
上级
85f52ebc
变更
6
隐藏空白更改
内联
并排
Showing
6 changed file
with
251 addition
and
0 deletion
+251
-0
src/db.c
src/db.c
+100
-0
src/dict.c
src/dict.c
+92
-0
src/dict.h
src/dict.h
+3
-0
src/redis.c
src/redis.c
+1
-0
src/redis.h
src/redis.h
+1
-0
tests/unit/basic.tcl
tests/unit/basic.tcl
+54
-0
未找到文件。
src/db.c
浏览文件 @
7f490b19
...
...
@@ -319,6 +319,106 @@ void keysCommand(redisClient *c) {
setDeferredMultiBulkLength
(
c
,
replylen
,
numkeys
);
}
void
scanCallback
(
void
*
privdata
,
const
dictEntry
*
de
)
{
list
*
keys
=
(
list
*
)
privdata
;
sds
key
=
dictGetKey
(
de
);
robj
*
kobj
=
createStringObject
(
key
,
sdslen
(
key
));
listAddNodeTail
(
keys
,
kobj
);
}
void
scanCommand
(
redisClient
*
c
)
{
int
rv
;
int
i
,
j
;
char
buf
[
32
];
list
*
keys
=
listCreate
();
listNode
*
ln
,
*
ln_
;
unsigned
long
cursor
=
0
;
long
count
=
1
;
sds
pat
;
int
patlen
,
patnoop
=
1
;
/* Use sscanf because we need an *unsigned* long */
rv
=
sscanf
(
c
->
argv
[
1
]
->
ptr
,
"%lu"
,
&
cursor
);
if
(
rv
!=
1
)
{
addReplyError
(
c
,
"invalid cursor"
);
goto
cleanup
;
}
i
=
2
;
while
(
i
<
c
->
argc
)
{
j
=
c
->
argc
-
i
;
if
(
!
strcasecmp
(
c
->
argv
[
i
]
->
ptr
,
"count"
)
&&
j
>=
2
)
{
if
(
getLongFromObjectOrReply
(
c
,
c
->
argv
[
i
+
1
],
&
count
,
NULL
)
!=
REDIS_OK
)
{
goto
cleanup
;
}
if
(
count
<
1
)
{
addReply
(
c
,
shared
.
syntaxerr
);
goto
cleanup
;
}
i
+=
2
;
}
else
if
(
!
strcasecmp
(
c
->
argv
[
i
]
->
ptr
,
"pattern"
)
&&
j
>=
2
)
{
pat
=
c
->
argv
[
i
+
1
]
->
ptr
;
patlen
=
sdslen
(
pat
);
/* The pattern is a no-op iff == "*" */
patnoop
=
(
pat
[
0
]
==
'*'
&&
patlen
==
1
);
i
+=
2
;
}
else
{
addReply
(
c
,
shared
.
syntaxerr
);
goto
cleanup
;
}
}
do
{
cursor
=
dictScan
(
c
->
db
->
dict
,
cursor
,
scanCallback
,
keys
);
}
while
(
cursor
&&
listLength
(
keys
)
<
count
);
/* Filter keys */
ln
=
listFirst
(
keys
);
while
(
ln
)
{
robj
*
kobj
=
listNodeValue
(
ln
);
ln_
=
listNextNode
(
ln
);
/* Keep key iff pattern matches and it hasn't expired */
if
((
patnoop
||
stringmatchlen
(
pat
,
patlen
,
kobj
->
ptr
,
sdslen
(
kobj
->
ptr
),
0
))
&&
(
expireIfNeeded
(
c
->
db
,
kobj
)
==
0
))
{
/* Keep */
}
else
{
decrRefCount
(
kobj
);
listDelNode
(
keys
,
ln
);
}
ln
=
ln_
;
}
addReplyMultiBulkLen
(
c
,
2
);
rv
=
snprintf
(
buf
,
sizeof
(
buf
),
"%lu"
,
cursor
);
redisAssert
(
rv
<
sizeof
(
buf
));
addReplyBulkCBuffer
(
c
,
buf
,
rv
);
addReplyMultiBulkLen
(
c
,
listLength
(
keys
));
while
((
ln
=
listFirst
(
keys
))
!=
NULL
)
{
robj
*
kobj
=
listNodeValue
(
ln
);
addReplyBulk
(
c
,
kobj
);
decrRefCount
(
kobj
);
listDelNode
(
keys
,
ln
);
}
cleanup:
while
((
ln
=
listFirst
(
keys
))
!=
NULL
)
{
robj
*
kobj
=
listNodeValue
(
ln
);
decrRefCount
(
kobj
);
listDelNode
(
keys
,
ln
);
}
listRelease
(
keys
);
}
void
dbsizeCommand
(
redisClient
*
c
)
{
addReplyLongLong
(
c
,
dictSize
(
c
->
db
->
dict
));
}
...
...
src/dict.c
浏览文件 @
7f490b19
...
...
@@ -648,6 +648,98 @@ dictEntry *dictGetRandomKey(dict *d)
return
he
;
}
/* Function to reverse bits. Algorithm from:
* http://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel */
static
unsigned
long
rev
(
unsigned
long
v
)
{
unsigned
long
s
=
8
*
sizeof
(
v
);
// bit size; must be power of 2
unsigned
long
mask
=
~
0
;
while
((
s
>>=
1
)
>
0
)
{
mask
^=
(
mask
<<
s
);
v
=
((
v
>>
s
)
&
mask
)
|
((
v
<<
s
)
&
~
mask
);
}
return
v
;
}
unsigned
long
dictScan
(
dict
*
d
,
unsigned
long
v
,
dictScanFunction
*
fn
,
void
*
privdata
)
{
dictht
*
t0
,
*
t1
;
const
dictEntry
*
de
;
unsigned
long
s0
,
s1
;
unsigned
long
m0
,
m1
;
if
(
!
dictIsRehashing
(
d
))
{
t0
=
&
(
d
->
ht
[
0
]);
m0
=
t0
->
sizemask
;
/* Emit entries at cursor */
de
=
t0
->
table
[
v
&
m0
];
while
(
de
)
{
fn
(
privdata
,
de
);
de
=
de
->
next
;
}
}
else
{
t0
=
&
d
->
ht
[
0
];
t1
=
&
d
->
ht
[
1
];
/* Make sure t0 is the smaller and t1 is the bigger table */
if
(
t0
->
size
>
t1
->
size
)
{
t0
=
&
d
->
ht
[
1
];
t1
=
&
d
->
ht
[
0
];
}
s0
=
t0
->
size
;
s1
=
t1
->
size
;
m0
=
t0
->
sizemask
;
m1
=
t1
->
sizemask
;
/* Emit entries at cursor */
de
=
t0
->
table
[
v
&
m0
];
while
(
de
)
{
fn
(
privdata
,
de
);
de
=
de
->
next
;
}
/* Iterate over indices in larger table that are the expansion
* of the index pointed to by the cursor in the smaller table */
do
{
/* Emit entries at cursor */
de
=
t1
->
table
[
v
&
m1
];
while
(
de
)
{
fn
(
privdata
,
de
);
de
=
de
->
next
;
}
/* Increment bits not covered by the smaller mask */
v
=
(((
v
|
m0
)
+
1
)
&
~
m0
)
|
(
v
&
m0
);
/* Continue while bits covered by mask difference is non-zero */
}
while
(
v
&
(
m0
^
m1
));
}
/* Set unmasked bits so incrementing the reversed cursor
* operates on the masked bits of the smaller table */
v
|=
~
m0
;
/* Increment the reverse cursor */
v
=
rev
(
v
);
v
++
;
v
=
rev
(
v
);
/* Only preprare cursor for the next iteration when it is non-zero,
* so that 0 can be used as end-of-scan sentinel. */
if
(
v
)
{
/* Set unmasked bits so the cursor will keep its position
* regardless of the mask in the next iterations */
v
|=
~
m0
;
}
return
v
;
}
/* ------------------------- private functions ------------------------------ */
/* Expand the hash table if needed */
...
...
src/dict.h
浏览文件 @
7f490b19
...
...
@@ -91,6 +91,8 @@ typedef struct dictIterator {
long
long
fingerprint
;
/* unsafe iterator fingerprint for misuse detection */
}
dictIterator
;
typedef
void
(
dictScanFunction
)(
void
*
privdata
,
const
dictEntry
*
de
);
/* This is the initial size of every hash table */
#define DICT_HT_INITIAL_SIZE 4
...
...
@@ -165,6 +167,7 @@ int dictRehash(dict *d, int n);
int
dictRehashMilliseconds
(
dict
*
d
,
int
ms
);
void
dictSetHashFunctionSeed
(
unsigned
int
initval
);
unsigned
int
dictGetHashFunctionSeed
(
void
);
unsigned
long
dictScan
(
dict
*
d
,
unsigned
long
v
,
dictScanFunction
*
fn
,
void
*
privdata
);
/* Hash table types */
extern
dictType
dictTypeHeapStringCopyKey
;
...
...
src/redis.c
浏览文件 @
7f490b19
...
...
@@ -210,6 +210,7 @@ struct redisCommand redisCommandTable[] = {
{
"pexpire"
,
pexpireCommand
,
3
,
"w"
,
0
,
NULL
,
1
,
1
,
1
,
0
,
0
},
{
"pexpireat"
,
pexpireatCommand
,
3
,
"w"
,
0
,
NULL
,
1
,
1
,
1
,
0
,
0
},
{
"keys"
,
keysCommand
,
2
,
"rS"
,
0
,
NULL
,
0
,
0
,
0
,
0
,
0
},
{
"scan"
,
scanCommand
,
-
1
,
"RS"
,
0
,
NULL
,
0
,
0
,
0
,
0
,
0
},
{
"dbsize"
,
dbsizeCommand
,
1
,
"r"
,
0
,
NULL
,
0
,
0
,
0
,
0
,
0
},
{
"auth"
,
authCommand
,
2
,
"rslt"
,
0
,
NULL
,
0
,
0
,
0
,
0
,
0
},
{
"ping"
,
pingCommand
,
1
,
"rt"
,
0
,
NULL
,
0
,
0
,
0
,
0
,
0
},
...
...
src/redis.h
浏览文件 @
7f490b19
...
...
@@ -1250,6 +1250,7 @@ void incrbyfloatCommand(redisClient *c);
void
selectCommand
(
redisClient
*
c
);
void
randomkeyCommand
(
redisClient
*
c
);
void
keysCommand
(
redisClient
*
c
);
void
scanCommand
(
redisClient
*
c
);
void
dbsizeCommand
(
redisClient
*
c
);
void
lastsaveCommand
(
redisClient
*
c
);
void
saveCommand
(
redisClient
*
c
);
...
...
tests/unit/basic.tcl
浏览文件 @
7f490b19
...
...
@@ -761,4 +761,58 @@ start_server {tags {"basic"}} {
r keys *
r keys *
}
{
dlskeriewrioeuwqoirueioqwrueoqwrueqw
}
test
"SCAN basic"
{
r flushdb
r debug populate 1000
set cur 0
set keys
{}
while 1
{
set res
[
r scan $cur
]
set cur
[
lindex $res 0
]
set k
[
lindex $res 1
]
lappend keys $k
if
{
$cur
== 0
}
break
}
set keys
[
lsort -unique
[
concat
{*}
$keys
]]
assert_equal 1000
[
llength $keys
]
}
test
"SCAN COUNT"
{
r flushdb
r debug populate 1000
set cur 0
set keys
{}
while 1
{
set res
[
r scan $cur count 5
]
set cur
[
lindex $res 0
]
set k
[
lindex $res 1
]
lappend keys $k
if
{
$cur
== 0
}
break
}
set keys
[
lsort -unique
[
concat
{*}
$keys
]]
assert_equal 1000
[
llength $keys
]
}
test
"SCAN PATTERN"
{
r flushdb
r debug populate 1000
set cur 0
set keys
{}
while 1
{
set res
[
r scan $cur pattern
"key:1??"
]
set cur
[
lindex $res 0
]
set k
[
lindex $res 1
]
lappend keys $k
if
{
$cur
== 0
}
break
}
set keys
[
lsort -unique
[
concat
{*}
$keys
]]
assert_equal 100
[
llength $keys
]
}
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录