redis-server 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (290) hide show
  1. data/LICENSE +38 -0
  2. data/README.md +33 -0
  3. data/bin/redis +114 -0
  4. data/redis/Makefile +5 -0
  5. data/redis/extconf.rb +3 -0
  6. data/redis/redis-2.2.11/00-RELEASENOTES +199 -0
  7. data/redis/redis-2.2.11/BUGS +1 -0
  8. data/redis/redis-2.2.11/CONTRIBUTING +13 -0
  9. data/redis/redis-2.2.11/COPYING +10 -0
  10. data/redis/redis-2.2.11/Changelog +1032 -0
  11. data/redis/redis-2.2.11/INSTALL +30 -0
  12. data/redis/redis-2.2.11/Makefile +22 -0
  13. data/redis/redis-2.2.11/README +83 -0
  14. data/redis/redis-2.2.11/TODO +4 -0
  15. data/redis/redis-2.2.11/client-libraries/README +11 -0
  16. data/redis/redis-2.2.11/deps/hiredis/COPYING +10 -0
  17. data/redis/redis-2.2.11/deps/hiredis/Makefile +115 -0
  18. data/redis/redis-2.2.11/deps/hiredis/README.md +311 -0
  19. data/redis/redis-2.2.11/deps/hiredis/TODO +2 -0
  20. data/redis/redis-2.2.11/deps/hiredis/adapters/ae.h +95 -0
  21. data/redis/redis-2.2.11/deps/hiredis/adapters/libev.h +113 -0
  22. data/redis/redis-2.2.11/deps/hiredis/adapters/libevent.h +76 -0
  23. data/redis/redis-2.2.11/deps/hiredis/async.c +321 -0
  24. data/redis/redis-2.2.11/deps/hiredis/async.h +112 -0
  25. data/redis/redis-2.2.11/deps/hiredis/example-ae.c +53 -0
  26. data/redis/redis-2.2.11/deps/hiredis/example-libev.c +47 -0
  27. data/redis/redis-2.2.11/deps/hiredis/example-libevent.c +48 -0
  28. data/redis/redis-2.2.11/deps/hiredis/example.c +67 -0
  29. data/redis/redis-2.2.11/deps/hiredis/fmacros.h +15 -0
  30. data/redis/redis-2.2.11/deps/hiredis/hiredis.c +1058 -0
  31. data/redis/redis-2.2.11/deps/hiredis/hiredis.h +170 -0
  32. data/redis/redis-2.2.11/deps/hiredis/net.c +170 -0
  33. data/redis/redis-2.2.11/deps/hiredis/net.h +43 -0
  34. data/redis/redis-2.2.11/deps/hiredis/sds.c +479 -0
  35. data/redis/redis-2.2.11/deps/hiredis/sds.h +77 -0
  36. data/redis/redis-2.2.11/deps/hiredis/test.c +479 -0
  37. data/redis/redis-2.2.11/deps/hiredis/util.h +40 -0
  38. data/redis/redis-2.2.11/deps/linenoise/Makefile +10 -0
  39. data/redis/redis-2.2.11/deps/linenoise/README.markdown +45 -0
  40. data/redis/redis-2.2.11/deps/linenoise/example.c +27 -0
  41. data/redis/redis-2.2.11/deps/linenoise/linenoise.c +609 -0
  42. data/redis/redis-2.2.11/deps/linenoise/linenoise.h +55 -0
  43. data/redis/redis-2.2.11/design-documents/REDIS-CLUSTER +214 -0
  44. data/redis/redis-2.2.11/design-documents/REDIS-CLUSTER-2 +343 -0
  45. data/redis/redis-2.2.11/doc/AppendCommand.html +48 -0
  46. data/redis/redis-2.2.11/doc/AppendOnlyFileHowto.html +41 -0
  47. data/redis/redis-2.2.11/doc/AuthCommand.html +39 -0
  48. data/redis/redis-2.2.11/doc/Benchmarks.html +129 -0
  49. data/redis/redis-2.2.11/doc/BgrewriteaofCommand.html +41 -0
  50. data/redis/redis-2.2.11/doc/BgsaveCommand.html +39 -0
  51. data/redis/redis-2.2.11/doc/BlpopCommand.html +51 -0
  52. data/redis/redis-2.2.11/doc/BrpoplpushCommand.html +39 -0
  53. data/redis/redis-2.2.11/doc/CommandReference.html +47 -0
  54. data/redis/redis-2.2.11/doc/Comparisons.html +42 -0
  55. data/redis/redis-2.2.11/doc/ConfigCommand.html +76 -0
  56. data/redis/redis-2.2.11/doc/Configuration.html +38 -0
  57. data/redis/redis-2.2.11/doc/ConnectionHandlingSidebar.html +36 -0
  58. data/redis/redis-2.2.11/doc/ControlCommandsSidebar.html +36 -0
  59. data/redis/redis-2.2.11/doc/Credits.html +38 -0
  60. data/redis/redis-2.2.11/doc/DbsizeCommand.html +38 -0
  61. data/redis/redis-2.2.11/doc/DelCommand.html +41 -0
  62. data/redis/redis-2.2.11/doc/DesignPatterns.html +37 -0
  63. data/redis/redis-2.2.11/doc/EventLibray.html +44 -0
  64. data/redis/redis-2.2.11/doc/ExistsCommand.html +42 -0
  65. data/redis/redis-2.2.11/doc/ExpireCommand.html +96 -0
  66. data/redis/redis-2.2.11/doc/FAQ.html +70 -0
  67. data/redis/redis-2.2.11/doc/Features.html +38 -0
  68. data/redis/redis-2.2.11/doc/FlushallCommand.html +39 -0
  69. data/redis/redis-2.2.11/doc/FlushdbCommand.html +39 -0
  70. data/redis/redis-2.2.11/doc/FromSqlToDataStructures.html +37 -0
  71. data/redis/redis-2.2.11/doc/GenericCommandsSidebar.html +36 -0
  72. data/redis/redis-2.2.11/doc/GetCommand.html +39 -0
  73. data/redis/redis-2.2.11/doc/GetbitCommand.html +39 -0
  74. data/redis/redis-2.2.11/doc/GetsetCommand.html +38 -0
  75. data/redis/redis-2.2.11/doc/HackingStrings.html +83 -0
  76. data/redis/redis-2.2.11/doc/HashCommandsSidebar.html +36 -0
  77. data/redis/redis-2.2.11/doc/Hashes.html +37 -0
  78. data/redis/redis-2.2.11/doc/HdelCommand.html +39 -0
  79. data/redis/redis-2.2.11/doc/HexistsCommand.html +39 -0
  80. data/redis/redis-2.2.11/doc/HgetCommand.html +39 -0
  81. data/redis/redis-2.2.11/doc/HgetallCommand.html +40 -0
  82. data/redis/redis-2.2.11/doc/HincrbyCommand.html +45 -0
  83. data/redis/redis-2.2.11/doc/HlenCommand.html +38 -0
  84. data/redis/redis-2.2.11/doc/HmgetCommand.html +40 -0
  85. data/redis/redis-2.2.11/doc/HmsetCommand.html +40 -0
  86. data/redis/redis-2.2.11/doc/HsetCommand.html +40 -0
  87. data/redis/redis-2.2.11/doc/HsetnxCommand.html +41 -0
  88. data/redis/redis-2.2.11/doc/IncrCommand.html +43 -0
  89. data/redis/redis-2.2.11/doc/InfoCommand.html +48 -0
  90. data/redis/redis-2.2.11/doc/IntroductionToRedisDataTypes.html +152 -0
  91. data/redis/redis-2.2.11/doc/KeysCommand.html +43 -0
  92. data/redis/redis-2.2.11/doc/LastsaveCommand.html +39 -0
  93. data/redis/redis-2.2.11/doc/LindexCommand.html +40 -0
  94. data/redis/redis-2.2.11/doc/ListCommandsSidebar.html +36 -0
  95. data/redis/redis-2.2.11/doc/Lists.html +42 -0
  96. data/redis/redis-2.2.11/doc/LlenCommand.html +41 -0
  97. data/redis/redis-2.2.11/doc/LpopCommand.html +41 -0
  98. data/redis/redis-2.2.11/doc/LrangeCommand.html +47 -0
  99. data/redis/redis-2.2.11/doc/LremCommand.html +41 -0
  100. data/redis/redis-2.2.11/doc/LsetCommand.html +38 -0
  101. data/redis/redis-2.2.11/doc/LtrimCommand.html +47 -0
  102. data/redis/redis-2.2.11/doc/MgetCommand.html +52 -0
  103. data/redis/redis-2.2.11/doc/MonitorCommand.html +63 -0
  104. data/redis/redis-2.2.11/doc/MoveCommand.html +42 -0
  105. data/redis/redis-2.2.11/doc/MsetCommand.html +44 -0
  106. data/redis/redis-2.2.11/doc/MultiExecCommand.html +166 -0
  107. data/redis/redis-2.2.11/doc/NonexistentCommands.html +51 -0
  108. data/redis/redis-2.2.11/doc/ObjectHashMappers.html +39 -0
  109. data/redis/redis-2.2.11/doc/Pipelining.html +36 -0
  110. data/redis/redis-2.2.11/doc/ProgrammingExamples.html +38 -0
  111. data/redis/redis-2.2.11/doc/ProtocolSpecification.html +137 -0
  112. data/redis/redis-2.2.11/doc/PublishSubscribe.html +115 -0
  113. data/redis/redis-2.2.11/doc/QuickStart.html +68 -0
  114. data/redis/redis-2.2.11/doc/QuitCommand.html +38 -0
  115. data/redis/redis-2.2.11/doc/README.html +119 -0
  116. data/redis/redis-2.2.11/doc/RandomkeyCommand.html +39 -0
  117. data/redis/redis-2.2.11/doc/Redis0100ChangeLog.html +67 -0
  118. data/redis/redis-2.2.11/doc/Redis0900ChangeLog.html +56 -0
  119. data/redis/redis-2.2.11/doc/RedisBigData.html +61 -0
  120. data/redis/redis-2.2.11/doc/RedisCLI.html +37 -0
  121. data/redis/redis-2.2.11/doc/RedisEventLibrary.html +70 -0
  122. data/redis/redis-2.2.11/doc/RedisGuides.html +37 -0
  123. data/redis/redis-2.2.11/doc/RedisInternals.html +38 -0
  124. data/redis/redis-2.2.11/doc/RedisPipelining.html +93 -0
  125. data/redis/redis-2.2.11/doc/RedisStatus.html +56 -0
  126. data/redis/redis-2.2.11/doc/Redis_1_2_0_Changelog.html +40 -0
  127. data/redis/redis-2.2.11/doc/Redis_2_0_0_Changelog.html +62 -0
  128. data/redis/redis-2.2.11/doc/Redis_2_0_Whats_new.html +59 -0
  129. data/redis/redis-2.2.11/doc/RenameCommand.html +39 -0
  130. data/redis/redis-2.2.11/doc/RenamenxCommand.html +42 -0
  131. data/redis/redis-2.2.11/doc/ReplicationHowto.html +43 -0
  132. data/redis/redis-2.2.11/doc/ReplyTypes.html +42 -0
  133. data/redis/redis-2.2.11/doc/RoadMap.html +38 -0
  134. data/redis/redis-2.2.11/doc/RpoplpushCommand.html +44 -0
  135. data/redis/redis-2.2.11/doc/RpushCommand.html +40 -0
  136. data/redis/redis-2.2.11/doc/SaddCommand.html +41 -0
  137. data/redis/redis-2.2.11/doc/SaveCommand.html +39 -0
  138. data/redis/redis-2.2.11/doc/ScardCommand.html +41 -0
  139. data/redis/redis-2.2.11/doc/SdiffCommand.html +45 -0
  140. data/redis/redis-2.2.11/doc/SdiffstoreCommand.html +38 -0
  141. data/redis/redis-2.2.11/doc/SelectCommand.html +39 -0
  142. data/redis/redis-2.2.11/doc/SetCommand.html +39 -0
  143. data/redis/redis-2.2.11/doc/SetCommandsSidebar.html +36 -0
  144. data/redis/redis-2.2.11/doc/SetbitCommand.html +45 -0
  145. data/redis/redis-2.2.11/doc/SetexCommand.html +42 -0
  146. data/redis/redis-2.2.11/doc/SetnxCommand.html +51 -0
  147. data/redis/redis-2.2.11/doc/SetrangeCommand.html +58 -0
  148. data/redis/redis-2.2.11/doc/Sets.html +36 -0
  149. data/redis/redis-2.2.11/doc/ShutdownCommand.html +39 -0
  150. data/redis/redis-2.2.11/doc/SideBar.html +36 -0
  151. data/redis/redis-2.2.11/doc/SinterCommand.html +40 -0
  152. data/redis/redis-2.2.11/doc/SinterstoreCommand.html +39 -0
  153. data/redis/redis-2.2.11/doc/SismemberCommand.html +42 -0
  154. data/redis/redis-2.2.11/doc/SlaveofCommand.html +41 -0
  155. data/redis/redis-2.2.11/doc/SmembersCommand.html +38 -0
  156. data/redis/redis-2.2.11/doc/SmoveCommand.html +44 -0
  157. data/redis/redis-2.2.11/doc/SortCommand.html +75 -0
  158. data/redis/redis-2.2.11/doc/SortedSetCommandsSidebar.html +36 -0
  159. data/redis/redis-2.2.11/doc/SortedSets.html +36 -0
  160. data/redis/redis-2.2.11/doc/Speed.html +38 -0
  161. data/redis/redis-2.2.11/doc/SponsorshipHistory.html +38 -0
  162. data/redis/redis-2.2.11/doc/SpopCommand.html +40 -0
  163. data/redis/redis-2.2.11/doc/SrandmemberCommand.html +40 -0
  164. data/redis/redis-2.2.11/doc/SremCommand.html +42 -0
  165. data/redis/redis-2.2.11/doc/StringCommandsSidebar.html +36 -0
  166. data/redis/redis-2.2.11/doc/Strings.html +37 -0
  167. data/redis/redis-2.2.11/doc/StrlenCommand.html +39 -0
  168. data/redis/redis-2.2.11/doc/SubstrCommand.html +52 -0
  169. data/redis/redis-2.2.11/doc/SunionCommand.html +40 -0
  170. data/redis/redis-2.2.11/doc/SunionstoreCommand.html +38 -0
  171. data/redis/redis-2.2.11/doc/SupportedLanguages.html +60 -0
  172. data/redis/redis-2.2.11/doc/SupportedPlatforms.html +37 -0
  173. data/redis/redis-2.2.11/doc/TemplateCommand.html +38 -0
  174. data/redis/redis-2.2.11/doc/TtlCommand.html +38 -0
  175. data/redis/redis-2.2.11/doc/TwitterAlikeExample.html +250 -0
  176. data/redis/redis-2.2.11/doc/TypeCommand.html +46 -0
  177. data/redis/redis-2.2.11/doc/UnstableSource.html +39 -0
  178. data/redis/redis-2.2.11/doc/VirtualMemorySpecification.html +156 -0
  179. data/redis/redis-2.2.11/doc/VirtualMemoryUserGuide.html +66 -0
  180. data/redis/redis-2.2.11/doc/ZaddCommand.html +43 -0
  181. data/redis/redis-2.2.11/doc/ZcardCommand.html +41 -0
  182. data/redis/redis-2.2.11/doc/ZincrbyCommand.html +42 -0
  183. data/redis/redis-2.2.11/doc/ZrangeCommand.html +42 -0
  184. data/redis/redis-2.2.11/doc/ZrangebyscoreCommand.html +77 -0
  185. data/redis/redis-2.2.11/doc/ZrankCommand.html +43 -0
  186. data/redis/redis-2.2.11/doc/ZremCommand.html +42 -0
  187. data/redis/redis-2.2.11/doc/ZremrangebyrankCommand.html +39 -0
  188. data/redis/redis-2.2.11/doc/ZremrangebyscoreCommand.html +39 -0
  189. data/redis/redis-2.2.11/doc/ZscoreCommand.html +41 -0
  190. data/redis/redis-2.2.11/doc/ZunionCommand.html +42 -0
  191. data/redis/redis-2.2.11/doc/ZunionstoreCommand.html +43 -0
  192. data/redis/redis-2.2.11/doc/index.html +43 -0
  193. data/redis/redis-2.2.11/doc/redis.png +0 -0
  194. data/redis/redis-2.2.11/doc/style.css +25 -0
  195. data/redis/redis-2.2.11/redis.conf +417 -0
  196. data/redis/redis-2.2.11/src/Makefile +177 -0
  197. data/redis/redis-2.2.11/src/adlist.c +325 -0
  198. data/redis/redis-2.2.11/src/adlist.h +92 -0
  199. data/redis/redis-2.2.11/src/ae.c +390 -0
  200. data/redis/redis-2.2.11/src/ae.h +117 -0
  201. data/redis/redis-2.2.11/src/ae_epoll.c +91 -0
  202. data/redis/redis-2.2.11/src/ae_kqueue.c +93 -0
  203. data/redis/redis-2.2.11/src/ae_select.c +72 -0
  204. data/redis/redis-2.2.11/src/anet.c +347 -0
  205. data/redis/redis-2.2.11/src/anet.h +57 -0
  206. data/redis/redis-2.2.11/src/aof.c +675 -0
  207. data/redis/redis-2.2.11/src/config.c +627 -0
  208. data/redis/redis-2.2.11/src/config.h +64 -0
  209. data/redis/redis-2.2.11/src/db.c +543 -0
  210. data/redis/redis-2.2.11/src/debug.c +314 -0
  211. data/redis/redis-2.2.11/src/dict.c +721 -0
  212. data/redis/redis-2.2.11/src/dict.h +156 -0
  213. data/redis/redis-2.2.11/src/fmacros.h +15 -0
  214. data/redis/redis-2.2.11/src/help.h +638 -0
  215. data/redis/redis-2.2.11/src/intset.c +422 -0
  216. data/redis/redis-2.2.11/src/intset.h +19 -0
  217. data/redis/redis-2.2.11/src/lzf.h +100 -0
  218. data/redis/redis-2.2.11/src/lzfP.h +159 -0
  219. data/redis/redis-2.2.11/src/lzf_c.c +295 -0
  220. data/redis/redis-2.2.11/src/lzf_d.c +150 -0
  221. data/redis/redis-2.2.11/src/mkreleasehdr.sh +9 -0
  222. data/redis/redis-2.2.11/src/multi.c +268 -0
  223. data/redis/redis-2.2.11/src/networking.c +899 -0
  224. data/redis/redis-2.2.11/src/object.c +484 -0
  225. data/redis/redis-2.2.11/src/pqsort.c +197 -0
  226. data/redis/redis-2.2.11/src/pqsort.h +15 -0
  227. data/redis/redis-2.2.11/src/pubsub.c +267 -0
  228. data/redis/redis-2.2.11/src/rdb.c +1020 -0
  229. data/redis/redis-2.2.11/src/redis-benchmark.c +530 -0
  230. data/redis/redis-2.2.11/src/redis-check-aof.c +185 -0
  231. data/redis/redis-2.2.11/src/redis-check-dump.c +681 -0
  232. data/redis/redis-2.2.11/src/redis-cli.c +773 -0
  233. data/redis/redis-2.2.11/src/redis.c +1677 -0
  234. data/redis/redis-2.2.11/src/redis.h +1022 -0
  235. data/redis/redis-2.2.11/src/release.c +13 -0
  236. data/redis/redis-2.2.11/src/replication.c +557 -0
  237. data/redis/redis-2.2.11/src/sds.c +639 -0
  238. data/redis/redis-2.2.11/src/sds.h +78 -0
  239. data/redis/redis-2.2.11/src/sha1.c +276 -0
  240. data/redis/redis-2.2.11/src/sha1.h +17 -0
  241. data/redis/redis-2.2.11/src/solarisfixes.h +22 -0
  242. data/redis/redis-2.2.11/src/sort.c +389 -0
  243. data/redis/redis-2.2.11/src/syncio.c +154 -0
  244. data/redis/redis-2.2.11/src/t_hash.c +476 -0
  245. data/redis/redis-2.2.11/src/t_list.c +986 -0
  246. data/redis/redis-2.2.11/src/t_set.c +610 -0
  247. data/redis/redis-2.2.11/src/t_string.c +438 -0
  248. data/redis/redis-2.2.11/src/t_zset.c +1084 -0
  249. data/redis/redis-2.2.11/src/testhelp.h +54 -0
  250. data/redis/redis-2.2.11/src/util.c +243 -0
  251. data/redis/redis-2.2.11/src/valgrind.sup +5 -0
  252. data/redis/redis-2.2.11/src/version.h +1 -0
  253. data/redis/redis-2.2.11/src/vm.c +1149 -0
  254. data/redis/redis-2.2.11/src/ziplist.c +1323 -0
  255. data/redis/redis-2.2.11/src/ziplist.h +15 -0
  256. data/redis/redis-2.2.11/src/zipmap.c +455 -0
  257. data/redis/redis-2.2.11/src/zipmap.h +48 -0
  258. data/redis/redis-2.2.11/src/zmalloc.c +278 -0
  259. data/redis/redis-2.2.11/src/zmalloc.h +47 -0
  260. data/redis/redis-2.2.11/tests/assets/default.conf +308 -0
  261. data/redis/redis-2.2.11/tests/integration/aof.tcl +104 -0
  262. data/redis/redis-2.2.11/tests/integration/redis-cli.tcl +208 -0
  263. data/redis/redis-2.2.11/tests/integration/replication.tcl +98 -0
  264. data/redis/redis-2.2.11/tests/support/redis.tcl +241 -0
  265. data/redis/redis-2.2.11/tests/support/server.tcl +294 -0
  266. data/redis/redis-2.2.11/tests/support/test.tcl +190 -0
  267. data/redis/redis-2.2.11/tests/support/tmpfile.tcl +15 -0
  268. data/redis/redis-2.2.11/tests/support/util.tcl +296 -0
  269. data/redis/redis-2.2.11/tests/test_helper.tcl +221 -0
  270. data/redis/redis-2.2.11/tests/unit/auth.tcl +15 -0
  271. data/redis/redis-2.2.11/tests/unit/basic.tcl +616 -0
  272. data/redis/redis-2.2.11/tests/unit/cas.tcl +135 -0
  273. data/redis/redis-2.2.11/tests/unit/expire.tcl +74 -0
  274. data/redis/redis-2.2.11/tests/unit/other.tcl +240 -0
  275. data/redis/redis-2.2.11/tests/unit/printver.tcl +6 -0
  276. data/redis/redis-2.2.11/tests/unit/protocol.tcl +62 -0
  277. data/redis/redis-2.2.11/tests/unit/pubsub.tcl +195 -0
  278. data/redis/redis-2.2.11/tests/unit/quit.tcl +40 -0
  279. data/redis/redis-2.2.11/tests/unit/sort.tcl +189 -0
  280. data/redis/redis-2.2.11/tests/unit/type/hash.tcl +300 -0
  281. data/redis/redis-2.2.11/tests/unit/type/list.tcl +819 -0
  282. data/redis/redis-2.2.11/tests/unit/type/set.tcl +334 -0
  283. data/redis/redis-2.2.11/tests/unit/type/zset.tcl +587 -0
  284. data/redis/redis-2.2.11/utils/build-static-symbols.tcl +22 -0
  285. data/redis/redis-2.2.11/utils/generate-command-help.rb +112 -0
  286. data/redis/redis-2.2.11/utils/mktarball.sh +13 -0
  287. data/redis/redis-2.2.11/utils/redis-copy.rb +78 -0
  288. data/redis/redis-2.2.11/utils/redis-sha1.rb +52 -0
  289. data/redis/redis-2.2.11/utils/redis_init_script +42 -0
  290. metadata +362 -0
@@ -0,0 +1,438 @@
1
+ #include "redis.h"
2
+
3
+ /*-----------------------------------------------------------------------------
4
+ * String Commands
5
+ *----------------------------------------------------------------------------*/
6
+
7
+ static int checkStringLength(redisClient *c, long long size) {
8
+ if (size > 512*1024*1024) {
9
+ addReplyError(c,"string exceeds maximum allowed size (512MB)");
10
+ return REDIS_ERR;
11
+ }
12
+ return REDIS_OK;
13
+ }
14
+
15
+ void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expire) {
16
+ int retval;
17
+ long seconds = 0; /* initialized to avoid an harmness warning */
18
+
19
+ if (expire) {
20
+ if (getLongFromObjectOrReply(c, expire, &seconds, NULL) != REDIS_OK)
21
+ return;
22
+ if (seconds <= 0) {
23
+ addReplyError(c,"invalid expire time in SETEX");
24
+ return;
25
+ }
26
+ }
27
+
28
+ lookupKeyWrite(c->db,key); /* Force expire of old key if needed */
29
+ retval = dbAdd(c->db,key,val);
30
+ if (retval == REDIS_ERR) {
31
+ if (!nx) {
32
+ dbReplace(c->db,key,val);
33
+ incrRefCount(val);
34
+ } else {
35
+ addReply(c,shared.czero);
36
+ return;
37
+ }
38
+ } else {
39
+ incrRefCount(val);
40
+ }
41
+ touchWatchedKey(c->db,key);
42
+ server.dirty++;
43
+ removeExpire(c->db,key);
44
+ if (expire) setExpire(c->db,key,time(NULL)+seconds);
45
+ addReply(c, nx ? shared.cone : shared.ok);
46
+ }
47
+
48
+ void setCommand(redisClient *c) {
49
+ c->argv[2] = tryObjectEncoding(c->argv[2]);
50
+ setGenericCommand(c,0,c->argv[1],c->argv[2],NULL);
51
+ }
52
+
53
+ void setnxCommand(redisClient *c) {
54
+ c->argv[2] = tryObjectEncoding(c->argv[2]);
55
+ setGenericCommand(c,1,c->argv[1],c->argv[2],NULL);
56
+ }
57
+
58
+ void setexCommand(redisClient *c) {
59
+ c->argv[3] = tryObjectEncoding(c->argv[3]);
60
+ setGenericCommand(c,0,c->argv[1],c->argv[3],c->argv[2]);
61
+ }
62
+
63
+ int getGenericCommand(redisClient *c) {
64
+ robj *o;
65
+
66
+ if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL)
67
+ return REDIS_OK;
68
+
69
+ if (o->type != REDIS_STRING) {
70
+ addReply(c,shared.wrongtypeerr);
71
+ return REDIS_ERR;
72
+ } else {
73
+ addReplyBulk(c,o);
74
+ return REDIS_OK;
75
+ }
76
+ }
77
+
78
+ void getCommand(redisClient *c) {
79
+ getGenericCommand(c);
80
+ }
81
+
82
+ void getsetCommand(redisClient *c) {
83
+ if (getGenericCommand(c) == REDIS_ERR) return;
84
+ c->argv[2] = tryObjectEncoding(c->argv[2]);
85
+ dbReplace(c->db,c->argv[1],c->argv[2]);
86
+ incrRefCount(c->argv[2]);
87
+ touchWatchedKey(c->db,c->argv[1]);
88
+ server.dirty++;
89
+ removeExpire(c->db,c->argv[1]);
90
+ }
91
+
92
+ static int getBitOffsetFromArgument(redisClient *c, robj *o, size_t *offset) {
93
+ long long loffset;
94
+ char *err = "bit offset is not an integer or out of range";
95
+
96
+ if (getLongLongFromObjectOrReply(c,o,&loffset,err) != REDIS_OK)
97
+ return REDIS_ERR;
98
+
99
+ /* Limit offset to 512MB in bytes */
100
+ if ((loffset < 0) || ((unsigned long long)loffset >> 3) >= (512*1024*1024))
101
+ {
102
+ addReplyError(c,err);
103
+ return REDIS_ERR;
104
+ }
105
+
106
+ *offset = (size_t)loffset;
107
+ return REDIS_OK;
108
+ }
109
+
110
+ void setbitCommand(redisClient *c) {
111
+ robj *o;
112
+ char *err = "bit is not an integer or out of range";
113
+ size_t bitoffset;
114
+ int byte, bit;
115
+ int byteval, bitval;
116
+ long on;
117
+
118
+ if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset) != REDIS_OK)
119
+ return;
120
+
121
+ if (getLongFromObjectOrReply(c,c->argv[3],&on,err) != REDIS_OK)
122
+ return;
123
+
124
+ /* Bits can only be set or cleared... */
125
+ if (on & ~1) {
126
+ addReplyError(c,err);
127
+ return;
128
+ }
129
+
130
+ o = lookupKeyWrite(c->db,c->argv[1]);
131
+ if (o == NULL) {
132
+ o = createObject(REDIS_STRING,sdsempty());
133
+ dbAdd(c->db,c->argv[1],o);
134
+ } else {
135
+ if (checkType(c,o,REDIS_STRING)) return;
136
+
137
+ /* Create a copy when the object is shared or encoded. */
138
+ if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) {
139
+ robj *decoded = getDecodedObject(o);
140
+ o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
141
+ decrRefCount(decoded);
142
+ dbReplace(c->db,c->argv[1],o);
143
+ }
144
+ }
145
+
146
+ /* Grow sds value to the right length if necessary */
147
+ byte = bitoffset >> 3;
148
+ o->ptr = sdsgrowzero(o->ptr,byte+1);
149
+
150
+ /* Get current values */
151
+ byteval = ((char*)o->ptr)[byte];
152
+ bit = 7 - (bitoffset & 0x7);
153
+ bitval = byteval & (1 << bit);
154
+
155
+ /* Update byte with new bit value and return original value */
156
+ byteval &= ~(1 << bit);
157
+ byteval |= ((on & 0x1) << bit);
158
+ ((char*)o->ptr)[byte] = byteval;
159
+ touchWatchedKey(c->db,c->argv[1]);
160
+ server.dirty++;
161
+ addReply(c, bitval ? shared.cone : shared.czero);
162
+ }
163
+
164
+ void getbitCommand(redisClient *c) {
165
+ robj *o;
166
+ char llbuf[32];
167
+ size_t bitoffset;
168
+ size_t byte, bit;
169
+ size_t bitval = 0;
170
+
171
+ if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset) != REDIS_OK)
172
+ return;
173
+
174
+ if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
175
+ checkType(c,o,REDIS_STRING)) return;
176
+
177
+ byte = bitoffset >> 3;
178
+ bit = 7 - (bitoffset & 0x7);
179
+ if (o->encoding != REDIS_ENCODING_RAW) {
180
+ if (byte < (size_t)ll2string(llbuf,sizeof(llbuf),(long)o->ptr))
181
+ bitval = llbuf[byte] & (1 << bit);
182
+ } else {
183
+ if (byte < sdslen(o->ptr))
184
+ bitval = ((char*)o->ptr)[byte] & (1 << bit);
185
+ }
186
+
187
+ addReply(c, bitval ? shared.cone : shared.czero);
188
+ }
189
+
190
+ void setrangeCommand(redisClient *c) {
191
+ robj *o;
192
+ long offset;
193
+ sds value = c->argv[3]->ptr;
194
+
195
+ if (getLongFromObjectOrReply(c,c->argv[2],&offset,NULL) != REDIS_OK)
196
+ return;
197
+
198
+ if (offset < 0) {
199
+ addReplyError(c,"offset is out of range");
200
+ return;
201
+ }
202
+
203
+ o = lookupKeyWrite(c->db,c->argv[1]);
204
+ if (o == NULL) {
205
+ /* Return 0 when setting nothing on a non-existing string */
206
+ if (sdslen(value) == 0) {
207
+ addReply(c,shared.czero);
208
+ return;
209
+ }
210
+
211
+ /* Return when the resulting string exceeds allowed size */
212
+ if (checkStringLength(c,offset+sdslen(value)) != REDIS_OK)
213
+ return;
214
+
215
+ o = createObject(REDIS_STRING,sdsempty());
216
+ dbAdd(c->db,c->argv[1],o);
217
+ } else {
218
+ size_t olen;
219
+
220
+ /* Key exists, check type */
221
+ if (checkType(c,o,REDIS_STRING))
222
+ return;
223
+
224
+ /* Return existing string length when setting nothing */
225
+ olen = stringObjectLen(o);
226
+ if (sdslen(value) == 0) {
227
+ addReplyLongLong(c,olen);
228
+ return;
229
+ }
230
+
231
+ /* Return when the resulting string exceeds allowed size */
232
+ if (checkStringLength(c,offset+sdslen(value)) != REDIS_OK)
233
+ return;
234
+
235
+ /* Create a copy when the object is shared or encoded. */
236
+ if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) {
237
+ robj *decoded = getDecodedObject(o);
238
+ o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
239
+ decrRefCount(decoded);
240
+ dbReplace(c->db,c->argv[1],o);
241
+ }
242
+ }
243
+
244
+ if (sdslen(value) > 0) {
245
+ o->ptr = sdsgrowzero(o->ptr,offset+sdslen(value));
246
+ memcpy((char*)o->ptr+offset,value,sdslen(value));
247
+ touchWatchedKey(c->db,c->argv[1]);
248
+ server.dirty++;
249
+ }
250
+ addReplyLongLong(c,sdslen(o->ptr));
251
+ }
252
+
253
+ void getrangeCommand(redisClient *c) {
254
+ robj *o;
255
+ long start, end;
256
+ char *str, llbuf[32];
257
+ size_t strlen;
258
+
259
+ if (getLongFromObjectOrReply(c,c->argv[2],&start,NULL) != REDIS_OK)
260
+ return;
261
+ if (getLongFromObjectOrReply(c,c->argv[3],&end,NULL) != REDIS_OK)
262
+ return;
263
+ if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptybulk)) == NULL ||
264
+ checkType(c,o,REDIS_STRING)) return;
265
+
266
+ if (o->encoding == REDIS_ENCODING_INT) {
267
+ str = llbuf;
268
+ strlen = ll2string(llbuf,sizeof(llbuf),(long)o->ptr);
269
+ } else {
270
+ str = o->ptr;
271
+ strlen = sdslen(str);
272
+ }
273
+
274
+ /* Convert negative indexes */
275
+ if (start < 0) start = strlen+start;
276
+ if (end < 0) end = strlen+end;
277
+ if (start < 0) start = 0;
278
+ if (end < 0) end = 0;
279
+ if ((unsigned)end >= strlen) end = strlen-1;
280
+
281
+ /* Precondition: end >= 0 && end < strlen, so the only condition where
282
+ * nothing can be returned is: start > end. */
283
+ if (start > end) {
284
+ addReply(c,shared.emptybulk);
285
+ } else {
286
+ addReplyBulkCBuffer(c,(char*)str+start,end-start+1);
287
+ }
288
+ }
289
+
290
+ void mgetCommand(redisClient *c) {
291
+ int j;
292
+
293
+ addReplyMultiBulkLen(c,c->argc-1);
294
+ for (j = 1; j < c->argc; j++) {
295
+ robj *o = lookupKeyRead(c->db,c->argv[j]);
296
+ if (o == NULL) {
297
+ addReply(c,shared.nullbulk);
298
+ } else {
299
+ if (o->type != REDIS_STRING) {
300
+ addReply(c,shared.nullbulk);
301
+ } else {
302
+ addReplyBulk(c,o);
303
+ }
304
+ }
305
+ }
306
+ }
307
+
308
+ void msetGenericCommand(redisClient *c, int nx) {
309
+ int j, busykeys = 0;
310
+
311
+ if ((c->argc % 2) == 0) {
312
+ addReplyError(c,"wrong number of arguments for MSET");
313
+ return;
314
+ }
315
+ /* Handle the NX flag. The MSETNX semantic is to return zero and don't
316
+ * set nothing at all if at least one already key exists. */
317
+ if (nx) {
318
+ for (j = 1; j < c->argc; j += 2) {
319
+ if (lookupKeyWrite(c->db,c->argv[j]) != NULL) {
320
+ busykeys++;
321
+ }
322
+ }
323
+ }
324
+ if (busykeys) {
325
+ addReply(c, shared.czero);
326
+ return;
327
+ }
328
+
329
+ for (j = 1; j < c->argc; j += 2) {
330
+ c->argv[j+1] = tryObjectEncoding(c->argv[j+1]);
331
+ dbReplace(c->db,c->argv[j],c->argv[j+1]);
332
+ incrRefCount(c->argv[j+1]);
333
+ removeExpire(c->db,c->argv[j]);
334
+ touchWatchedKey(c->db,c->argv[j]);
335
+ }
336
+ server.dirty += (c->argc-1)/2;
337
+ addReply(c, nx ? shared.cone : shared.ok);
338
+ }
339
+
340
+ void msetCommand(redisClient *c) {
341
+ msetGenericCommand(c,0);
342
+ }
343
+
344
+ void msetnxCommand(redisClient *c) {
345
+ msetGenericCommand(c,1);
346
+ }
347
+
348
+ void incrDecrCommand(redisClient *c, long long incr) {
349
+ long long value, oldvalue;
350
+ robj *o;
351
+
352
+ o = lookupKeyWrite(c->db,c->argv[1]);
353
+ if (o != NULL && checkType(c,o,REDIS_STRING)) return;
354
+ if (getLongLongFromObjectOrReply(c,o,&value,NULL) != REDIS_OK) return;
355
+
356
+ oldvalue = value;
357
+ value += incr;
358
+ if ((incr < 0 && value > oldvalue) || (incr > 0 && value < oldvalue)) {
359
+ addReplyError(c,"increment or decrement would overflow");
360
+ return;
361
+ }
362
+ o = createStringObjectFromLongLong(value);
363
+ dbReplace(c->db,c->argv[1],o);
364
+ touchWatchedKey(c->db,c->argv[1]);
365
+ server.dirty++;
366
+ addReply(c,shared.colon);
367
+ addReply(c,o);
368
+ addReply(c,shared.crlf);
369
+ }
370
+
371
+ void incrCommand(redisClient *c) {
372
+ incrDecrCommand(c,1);
373
+ }
374
+
375
+ void decrCommand(redisClient *c) {
376
+ incrDecrCommand(c,-1);
377
+ }
378
+
379
+ void incrbyCommand(redisClient *c) {
380
+ long long incr;
381
+
382
+ if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return;
383
+ incrDecrCommand(c,incr);
384
+ }
385
+
386
+ void decrbyCommand(redisClient *c) {
387
+ long long incr;
388
+
389
+ if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return;
390
+ incrDecrCommand(c,-incr);
391
+ }
392
+
393
+ void appendCommand(redisClient *c) {
394
+ size_t totlen;
395
+ robj *o, *append;
396
+
397
+ o = lookupKeyWrite(c->db,c->argv[1]);
398
+ if (o == NULL) {
399
+ /* Create the key */
400
+ c->argv[2] = tryObjectEncoding(c->argv[2]);
401
+ dbAdd(c->db,c->argv[1],c->argv[2]);
402
+ incrRefCount(c->argv[2]);
403
+ totlen = stringObjectLen(c->argv[2]);
404
+ } else {
405
+ /* Key exists, check type */
406
+ if (checkType(c,o,REDIS_STRING))
407
+ return;
408
+
409
+ /* "append" is an argument, so always an sds */
410
+ append = c->argv[2];
411
+ totlen = stringObjectLen(o)+sdslen(append->ptr);
412
+ if (checkStringLength(c,totlen) != REDIS_OK)
413
+ return;
414
+
415
+ /* If the object is shared or encoded, we have to make a copy */
416
+ if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) {
417
+ robj *decoded = getDecodedObject(o);
418
+ o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
419
+ decrRefCount(decoded);
420
+ dbReplace(c->db,c->argv[1],o);
421
+ }
422
+
423
+ /* Append the value */
424
+ o->ptr = sdscatlen(o->ptr,append->ptr,sdslen(append->ptr));
425
+ totlen = sdslen(o->ptr);
426
+ }
427
+ touchWatchedKey(c->db,c->argv[1]);
428
+ server.dirty++;
429
+ addReplyLongLong(c,totlen);
430
+ }
431
+
432
+ void strlenCommand(redisClient *c) {
433
+ robj *o;
434
+ if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
435
+ checkType(c,o,REDIS_STRING)) return;
436
+ addReplyLongLong(c,stringObjectLen(o));
437
+ }
438
+
@@ -0,0 +1,1084 @@
1
+ #include "redis.h"
2
+
3
+ #include <math.h>
4
+
5
+ /*-----------------------------------------------------------------------------
6
+ * Sorted set API
7
+ *----------------------------------------------------------------------------*/
8
+
9
+ /* ZSETs are ordered sets using two data structures to hold the same elements
10
+ * in order to get O(log(N)) INSERT and REMOVE operations into a sorted
11
+ * data structure.
12
+ *
13
+ * The elements are added to an hash table mapping Redis objects to scores.
14
+ * At the same time the elements are added to a skip list mapping scores
15
+ * to Redis objects (so objects are sorted by scores in this "view"). */
16
+
17
+ /* This skiplist implementation is almost a C translation of the original
18
+ * algorithm described by William Pugh in "Skip Lists: A Probabilistic
19
+ * Alternative to Balanced Trees", modified in three ways:
20
+ * a) this implementation allows for repeated values.
21
+ * b) the comparison is not just by key (our 'score') but by satellite data.
22
+ * c) there is a back pointer, so it's a doubly linked list with the back
23
+ * pointers being only at "level 1". This allows to traverse the list
24
+ * from tail to head, useful for ZREVRANGE. */
25
+
26
+ zskiplistNode *zslCreateNode(int level, double score, robj *obj) {
27
+ zskiplistNode *zn = zmalloc(sizeof(*zn)+level*sizeof(struct zskiplistLevel));
28
+ zn->score = score;
29
+ zn->obj = obj;
30
+ return zn;
31
+ }
32
+
33
+ zskiplist *zslCreate(void) {
34
+ int j;
35
+ zskiplist *zsl;
36
+
37
+ zsl = zmalloc(sizeof(*zsl));
38
+ zsl->level = 1;
39
+ zsl->length = 0;
40
+ zsl->header = zslCreateNode(ZSKIPLIST_MAXLEVEL,0,NULL);
41
+ for (j = 0; j < ZSKIPLIST_MAXLEVEL; j++) {
42
+ zsl->header->level[j].forward = NULL;
43
+ zsl->header->level[j].span = 0;
44
+ }
45
+ zsl->header->backward = NULL;
46
+ zsl->tail = NULL;
47
+ return zsl;
48
+ }
49
+
50
+ void zslFreeNode(zskiplistNode *node) {
51
+ decrRefCount(node->obj);
52
+ zfree(node);
53
+ }
54
+
55
+ void zslFree(zskiplist *zsl) {
56
+ zskiplistNode *node = zsl->header->level[0].forward, *next;
57
+
58
+ zfree(zsl->header);
59
+ while(node) {
60
+ next = node->level[0].forward;
61
+ zslFreeNode(node);
62
+ node = next;
63
+ }
64
+ zfree(zsl);
65
+ }
66
+
67
+ int zslRandomLevel(void) {
68
+ int level = 1;
69
+ while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF))
70
+ level += 1;
71
+ return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;
72
+ }
73
+
74
+ zskiplistNode *zslInsert(zskiplist *zsl, double score, robj *obj) {
75
+ zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;
76
+ unsigned int rank[ZSKIPLIST_MAXLEVEL];
77
+ int i, level;
78
+
79
+ x = zsl->header;
80
+ for (i = zsl->level-1; i >= 0; i--) {
81
+ /* store rank that is crossed to reach the insert position */
82
+ rank[i] = i == (zsl->level-1) ? 0 : rank[i+1];
83
+ while (x->level[i].forward &&
84
+ (x->level[i].forward->score < score ||
85
+ (x->level[i].forward->score == score &&
86
+ compareStringObjects(x->level[i].forward->obj,obj) < 0))) {
87
+ rank[i] += x->level[i].span;
88
+ x = x->level[i].forward;
89
+ }
90
+ update[i] = x;
91
+ }
92
+ /* we assume the key is not already inside, since we allow duplicated
93
+ * scores, and the re-insertion of score and redis object should never
94
+ * happpen since the caller of zslInsert() should test in the hash table
95
+ * if the element is already inside or not. */
96
+ level = zslRandomLevel();
97
+ if (level > zsl->level) {
98
+ for (i = zsl->level; i < level; i++) {
99
+ rank[i] = 0;
100
+ update[i] = zsl->header;
101
+ update[i]->level[i].span = zsl->length;
102
+ }
103
+ zsl->level = level;
104
+ }
105
+ x = zslCreateNode(level,score,obj);
106
+ for (i = 0; i < level; i++) {
107
+ x->level[i].forward = update[i]->level[i].forward;
108
+ update[i]->level[i].forward = x;
109
+
110
+ /* update span covered by update[i] as x is inserted here */
111
+ x->level[i].span = update[i]->level[i].span - (rank[0] - rank[i]);
112
+ update[i]->level[i].span = (rank[0] - rank[i]) + 1;
113
+ }
114
+
115
+ /* increment span for untouched levels */
116
+ for (i = level; i < zsl->level; i++) {
117
+ update[i]->level[i].span++;
118
+ }
119
+
120
+ x->backward = (update[0] == zsl->header) ? NULL : update[0];
121
+ if (x->level[0].forward)
122
+ x->level[0].forward->backward = x;
123
+ else
124
+ zsl->tail = x;
125
+ zsl->length++;
126
+ return x;
127
+ }
128
+
129
+ /* Internal function used by zslDelete, zslDeleteByScore and zslDeleteByRank */
130
+ void zslDeleteNode(zskiplist *zsl, zskiplistNode *x, zskiplistNode **update) {
131
+ int i;
132
+ for (i = 0; i < zsl->level; i++) {
133
+ if (update[i]->level[i].forward == x) {
134
+ update[i]->level[i].span += x->level[i].span - 1;
135
+ update[i]->level[i].forward = x->level[i].forward;
136
+ } else {
137
+ update[i]->level[i].span -= 1;
138
+ }
139
+ }
140
+ if (x->level[0].forward) {
141
+ x->level[0].forward->backward = x->backward;
142
+ } else {
143
+ zsl->tail = x->backward;
144
+ }
145
+ while(zsl->level > 1 && zsl->header->level[zsl->level-1].forward == NULL)
146
+ zsl->level--;
147
+ zsl->length--;
148
+ }
149
+
150
+ /* Delete an element with matching score/object from the skiplist. */
151
+ int zslDelete(zskiplist *zsl, double score, robj *obj) {
152
+ zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;
153
+ int i;
154
+
155
+ x = zsl->header;
156
+ for (i = zsl->level-1; i >= 0; i--) {
157
+ while (x->level[i].forward &&
158
+ (x->level[i].forward->score < score ||
159
+ (x->level[i].forward->score == score &&
160
+ compareStringObjects(x->level[i].forward->obj,obj) < 0)))
161
+ x = x->level[i].forward;
162
+ update[i] = x;
163
+ }
164
+ /* We may have multiple elements with the same score, what we need
165
+ * is to find the element with both the right score and object. */
166
+ x = x->level[0].forward;
167
+ if (x && score == x->score && equalStringObjects(x->obj,obj)) {
168
+ zslDeleteNode(zsl, x, update);
169
+ zslFreeNode(x);
170
+ return 1;
171
+ } else {
172
+ return 0; /* not found */
173
+ }
174
+ return 0; /* not found */
175
+ }
176
+
177
+ /* Struct to hold a inclusive/exclusive range spec. */
178
+ typedef struct {
179
+ double min, max;
180
+ int minex, maxex; /* are min or max exclusive? */
181
+ } zrangespec;
182
+
183
+ /* Delete all the elements with score between min and max from the skiplist.
184
+ * Min and mx are inclusive, so a score >= min || score <= max is deleted.
185
+ * Note that this function takes the reference to the hash table view of the
186
+ * sorted set, in order to remove the elements from the hash table too. */
187
+ unsigned long zslDeleteRangeByScore(zskiplist *zsl, zrangespec range, dict *dict) {
188
+ zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;
189
+ unsigned long removed = 0;
190
+ int i;
191
+
192
+ x = zsl->header;
193
+ for (i = zsl->level-1; i >= 0; i--) {
194
+ while (x->level[i].forward && (range.minex ?
195
+ x->level[i].forward->score <= range.min :
196
+ x->level[i].forward->score < range.min))
197
+ x = x->level[i].forward;
198
+ update[i] = x;
199
+ }
200
+
201
+ /* Current node is the last with score < or <= min. */
202
+ x = x->level[0].forward;
203
+
204
+ /* Delete nodes while in range. */
205
+ while (x && (range.maxex ? x->score < range.max : x->score <= range.max)) {
206
+ zskiplistNode *next = x->level[0].forward;
207
+ zslDeleteNode(zsl,x,update);
208
+ dictDelete(dict,x->obj);
209
+ zslFreeNode(x);
210
+ removed++;
211
+ x = next;
212
+ }
213
+ return removed;
214
+ }
215
+
216
+ /* Delete all the elements with rank between start and end from the skiplist.
217
+ * Start and end are inclusive. Note that start and end need to be 1-based */
218
+ unsigned long zslDeleteRangeByRank(zskiplist *zsl, unsigned int start, unsigned int end, dict *dict) {
219
+ zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;
220
+ unsigned long traversed = 0, removed = 0;
221
+ int i;
222
+
223
+ x = zsl->header;
224
+ for (i = zsl->level-1; i >= 0; i--) {
225
+ while (x->level[i].forward && (traversed + x->level[i].span) < start) {
226
+ traversed += x->level[i].span;
227
+ x = x->level[i].forward;
228
+ }
229
+ update[i] = x;
230
+ }
231
+
232
+ traversed++;
233
+ x = x->level[0].forward;
234
+ while (x && traversed <= end) {
235
+ zskiplistNode *next = x->level[0].forward;
236
+ zslDeleteNode(zsl,x,update);
237
+ dictDelete(dict,x->obj);
238
+ zslFreeNode(x);
239
+ removed++;
240
+ traversed++;
241
+ x = next;
242
+ }
243
+ return removed;
244
+ }
245
+
246
+ /* Find the first node having a score equal or greater than the specified one.
247
+ * Returns NULL if there is no match. */
248
+ zskiplistNode *zslFirstWithScore(zskiplist *zsl, double score) {
249
+ zskiplistNode *x;
250
+ int i;
251
+
252
+ x = zsl->header;
253
+ for (i = zsl->level-1; i >= 0; i--) {
254
+ while (x->level[i].forward && x->level[i].forward->score < score)
255
+ x = x->level[i].forward;
256
+ }
257
+ /* We may have multiple elements with the same score, what we need
258
+ * is to find the element with both the right score and object. */
259
+ return x->level[0].forward;
260
+ }
261
+
262
+ /* Find the rank for an element by both score and key.
263
+ * Returns 0 when the element cannot be found, rank otherwise.
264
+ * Note that the rank is 1-based due to the span of zsl->header to the
265
+ * first element. */
266
+ unsigned long zslGetRank(zskiplist *zsl, double score, robj *o) {
267
+ zskiplistNode *x;
268
+ unsigned long rank = 0;
269
+ int i;
270
+
271
+ x = zsl->header;
272
+ for (i = zsl->level-1; i >= 0; i--) {
273
+ while (x->level[i].forward &&
274
+ (x->level[i].forward->score < score ||
275
+ (x->level[i].forward->score == score &&
276
+ compareStringObjects(x->level[i].forward->obj,o) <= 0))) {
277
+ rank += x->level[i].span;
278
+ x = x->level[i].forward;
279
+ }
280
+
281
+ /* x might be equal to zsl->header, so test if obj is non-NULL */
282
+ if (x->obj && equalStringObjects(x->obj,o)) {
283
+ return rank;
284
+ }
285
+ }
286
+ return 0;
287
+ }
288
+
289
+ /* Finds an element by its rank. The rank argument needs to be 1-based. */
290
+ zskiplistNode* zslGetElementByRank(zskiplist *zsl, unsigned long rank) {
291
+ zskiplistNode *x;
292
+ unsigned long traversed = 0;
293
+ int i;
294
+
295
+ x = zsl->header;
296
+ for (i = zsl->level-1; i >= 0; i--) {
297
+ while (x->level[i].forward && (traversed + x->level[i].span) <= rank)
298
+ {
299
+ traversed += x->level[i].span;
300
+ x = x->level[i].forward;
301
+ }
302
+ if (traversed == rank) {
303
+ return x;
304
+ }
305
+ }
306
+ return NULL;
307
+ }
308
+
309
+ /* Populate the rangespec according to the objects min and max. */
310
+ static int zslParseRange(robj *min, robj *max, zrangespec *spec) {
311
+ char *eptr;
312
+ spec->minex = spec->maxex = 0;
313
+
314
+ /* Parse the min-max interval. If one of the values is prefixed
315
+ * by the "(" character, it's considered "open". For instance
316
+ * ZRANGEBYSCORE zset (1.5 (2.5 will match min < x < max
317
+ * ZRANGEBYSCORE zset 1.5 2.5 will instead match min <= x <= max */
318
+ if (min->encoding == REDIS_ENCODING_INT) {
319
+ spec->min = (long)min->ptr;
320
+ } else {
321
+ if (((char*)min->ptr)[0] == '(') {
322
+ spec->min = strtod((char*)min->ptr+1,&eptr);
323
+ if (eptr[0] != '\0' || isnan(spec->min)) return REDIS_ERR;
324
+ spec->minex = 1;
325
+ } else {
326
+ spec->min = strtod((char*)min->ptr,&eptr);
327
+ if (eptr[0] != '\0' || isnan(spec->min)) return REDIS_ERR;
328
+ }
329
+ }
330
+ if (max->encoding == REDIS_ENCODING_INT) {
331
+ spec->max = (long)max->ptr;
332
+ } else {
333
+ if (((char*)max->ptr)[0] == '(') {
334
+ spec->max = strtod((char*)max->ptr+1,&eptr);
335
+ if (eptr[0] != '\0' || isnan(spec->max)) return REDIS_ERR;
336
+ spec->maxex = 1;
337
+ } else {
338
+ spec->max = strtod((char*)max->ptr,&eptr);
339
+ if (eptr[0] != '\0' || isnan(spec->max)) return REDIS_ERR;
340
+ }
341
+ }
342
+
343
+ return REDIS_OK;
344
+ }
345
+
346
+
347
+ /*-----------------------------------------------------------------------------
348
+ * Sorted set commands
349
+ *----------------------------------------------------------------------------*/
350
+
351
+ /* This generic command implements both ZADD and ZINCRBY. */
352
+ void zaddGenericCommand(redisClient *c, robj *key, robj *ele, double score, int incr) {
353
+ robj *zsetobj;
354
+ zset *zs;
355
+ zskiplistNode *znode;
356
+
357
+ zsetobj = lookupKeyWrite(c->db,key);
358
+ if (zsetobj == NULL) {
359
+ zsetobj = createZsetObject();
360
+ dbAdd(c->db,key,zsetobj);
361
+ } else {
362
+ if (zsetobj->type != REDIS_ZSET) {
363
+ addReply(c,shared.wrongtypeerr);
364
+ return;
365
+ }
366
+ }
367
+ zs = zsetobj->ptr;
368
+
369
+ /* Since both ZADD and ZINCRBY are implemented here, we need to increment
370
+ * the score first by the current score if ZINCRBY is called. */
371
+ if (incr) {
372
+ /* Read the old score. If the element was not present starts from 0 */
373
+ dictEntry *de = dictFind(zs->dict,ele);
374
+ if (de != NULL)
375
+ score += *(double*)dictGetEntryVal(de);
376
+
377
+ if (isnan(score)) {
378
+ addReplyError(c,"resulting score is not a number (NaN)");
379
+ /* Note that we don't need to check if the zset may be empty and
380
+ * should be removed here, as we can only obtain Nan as score if
381
+ * there was already an element in the sorted set. */
382
+ return;
383
+ }
384
+ }
385
+
386
+ /* We need to remove and re-insert the element when it was already present
387
+ * in the dictionary, to update the skiplist. Note that we delay adding a
388
+ * pointer to the score because we want to reference the score in the
389
+ * skiplist node. */
390
+ if (dictAdd(zs->dict,ele,NULL) == DICT_OK) {
391
+ dictEntry *de;
392
+
393
+ /* New element */
394
+ incrRefCount(ele); /* added to hash */
395
+ znode = zslInsert(zs->zsl,score,ele);
396
+ incrRefCount(ele); /* added to skiplist */
397
+
398
+ /* Update the score in the dict entry */
399
+ de = dictFind(zs->dict,ele);
400
+ redisAssert(de != NULL);
401
+ dictGetEntryVal(de) = &znode->score;
402
+ touchWatchedKey(c->db,c->argv[1]);
403
+ server.dirty++;
404
+ if (incr)
405
+ addReplyDouble(c,score);
406
+ else
407
+ addReply(c,shared.cone);
408
+ } else {
409
+ dictEntry *de;
410
+ robj *curobj;
411
+ double *curscore;
412
+ int deleted;
413
+
414
+ /* Update score */
415
+ de = dictFind(zs->dict,ele);
416
+ redisAssert(de != NULL);
417
+ curobj = dictGetEntryKey(de);
418
+ curscore = dictGetEntryVal(de);
419
+
420
+ /* When the score is updated, reuse the existing string object to
421
+ * prevent extra alloc/dealloc of strings on ZINCRBY. */
422
+ if (score != *curscore) {
423
+ deleted = zslDelete(zs->zsl,*curscore,curobj);
424
+ redisAssert(deleted != 0);
425
+ znode = zslInsert(zs->zsl,score,curobj);
426
+ incrRefCount(curobj);
427
+
428
+ /* Update the score in the current dict entry */
429
+ dictGetEntryVal(de) = &znode->score;
430
+ touchWatchedKey(c->db,c->argv[1]);
431
+ server.dirty++;
432
+ }
433
+ if (incr)
434
+ addReplyDouble(c,score);
435
+ else
436
+ addReply(c,shared.czero);
437
+ }
438
+ }
439
+
440
+ void zaddCommand(redisClient *c) {
441
+ double scoreval;
442
+ if (getDoubleFromObjectOrReply(c,c->argv[2],&scoreval,NULL) != REDIS_OK) return;
443
+ c->argv[3] = tryObjectEncoding(c->argv[3]);
444
+ zaddGenericCommand(c,c->argv[1],c->argv[3],scoreval,0);
445
+ }
446
+
447
+ void zincrbyCommand(redisClient *c) {
448
+ double scoreval;
449
+ if (getDoubleFromObjectOrReply(c,c->argv[2],&scoreval,NULL) != REDIS_OK) return;
450
+ c->argv[3] = tryObjectEncoding(c->argv[3]);
451
+ zaddGenericCommand(c,c->argv[1],c->argv[3],scoreval,1);
452
+ }
453
+
454
+ void zremCommand(redisClient *c) {
455
+ robj *zsetobj;
456
+ zset *zs;
457
+ dictEntry *de;
458
+ double curscore;
459
+ int deleted;
460
+
461
+ if ((zsetobj = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
462
+ checkType(c,zsetobj,REDIS_ZSET)) return;
463
+
464
+ zs = zsetobj->ptr;
465
+ c->argv[2] = tryObjectEncoding(c->argv[2]);
466
+ de = dictFind(zs->dict,c->argv[2]);
467
+ if (de == NULL) {
468
+ addReply(c,shared.czero);
469
+ return;
470
+ }
471
+ /* Delete from the skiplist */
472
+ curscore = *(double*)dictGetEntryVal(de);
473
+ deleted = zslDelete(zs->zsl,curscore,c->argv[2]);
474
+ redisAssert(deleted != 0);
475
+
476
+ /* Delete from the hash table */
477
+ dictDelete(zs->dict,c->argv[2]);
478
+ if (htNeedsResize(zs->dict)) dictResize(zs->dict);
479
+ if (dictSize(zs->dict) == 0) dbDelete(c->db,c->argv[1]);
480
+ touchWatchedKey(c->db,c->argv[1]);
481
+ server.dirty++;
482
+ addReply(c,shared.cone);
483
+ }
484
+
485
+ void zremrangebyscoreCommand(redisClient *c) {
486
+ zrangespec range;
487
+ long deleted;
488
+ robj *o;
489
+ zset *zs;
490
+
491
+ /* Parse the range arguments. */
492
+ if (zslParseRange(c->argv[2],c->argv[3],&range) != REDIS_OK) {
493
+ addReplyError(c,"min or max is not a double");
494
+ return;
495
+ }
496
+
497
+ if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
498
+ checkType(c,o,REDIS_ZSET)) return;
499
+
500
+ zs = o->ptr;
501
+ deleted = zslDeleteRangeByScore(zs->zsl,range,zs->dict);
502
+ if (htNeedsResize(zs->dict)) dictResize(zs->dict);
503
+ if (dictSize(zs->dict) == 0) dbDelete(c->db,c->argv[1]);
504
+ if (deleted) touchWatchedKey(c->db,c->argv[1]);
505
+ server.dirty += deleted;
506
+ addReplyLongLong(c,deleted);
507
+ }
508
+
509
+ void zremrangebyrankCommand(redisClient *c) {
510
+ long start;
511
+ long end;
512
+ int llen;
513
+ long deleted;
514
+ robj *zsetobj;
515
+ zset *zs;
516
+
517
+ if ((getLongFromObjectOrReply(c, c->argv[2], &start, NULL) != REDIS_OK) ||
518
+ (getLongFromObjectOrReply(c, c->argv[3], &end, NULL) != REDIS_OK)) return;
519
+
520
+ if ((zsetobj = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
521
+ checkType(c,zsetobj,REDIS_ZSET)) return;
522
+ zs = zsetobj->ptr;
523
+ llen = zs->zsl->length;
524
+
525
+ /* convert negative indexes */
526
+ if (start < 0) start = llen+start;
527
+ if (end < 0) end = llen+end;
528
+ if (start < 0) start = 0;
529
+
530
+ /* Invariant: start >= 0, so this test will be true when end < 0.
531
+ * The range is empty when start > end or start >= length. */
532
+ if (start > end || start >= llen) {
533
+ addReply(c,shared.czero);
534
+ return;
535
+ }
536
+ if (end >= llen) end = llen-1;
537
+
538
+ /* increment start and end because zsl*Rank functions
539
+ * use 1-based rank */
540
+ deleted = zslDeleteRangeByRank(zs->zsl,start+1,end+1,zs->dict);
541
+ if (htNeedsResize(zs->dict)) dictResize(zs->dict);
542
+ if (dictSize(zs->dict) == 0) dbDelete(c->db,c->argv[1]);
543
+ if (deleted) touchWatchedKey(c->db,c->argv[1]);
544
+ server.dirty += deleted;
545
+ addReplyLongLong(c, deleted);
546
+ }
547
+
548
+ typedef struct {
549
+ dict *dict;
550
+ double weight;
551
+ } zsetopsrc;
552
+
553
+ int qsortCompareZsetopsrcByCardinality(const void *s1, const void *s2) {
554
+ zsetopsrc *d1 = (void*) s1, *d2 = (void*) s2;
555
+ unsigned long size1, size2;
556
+ size1 = d1->dict ? dictSize(d1->dict) : 0;
557
+ size2 = d2->dict ? dictSize(d2->dict) : 0;
558
+ return size1 - size2;
559
+ }
560
+
561
+ #define REDIS_AGGR_SUM 1
562
+ #define REDIS_AGGR_MIN 2
563
+ #define REDIS_AGGR_MAX 3
564
+ #define zunionInterDictValue(_e) (dictGetEntryVal(_e) == NULL ? 1.0 : *(double*)dictGetEntryVal(_e))
565
+
566
+ inline static void zunionInterAggregate(double *target, double val, int aggregate) {
567
+ if (aggregate == REDIS_AGGR_SUM) {
568
+ *target = *target + val;
569
+ /* The result of adding two doubles is NaN when one variable
570
+ * is +inf and the other is -inf. When these numbers are added,
571
+ * we maintain the convention of the result being 0.0. */
572
+ if (isnan(*target)) *target = 0.0;
573
+ } else if (aggregate == REDIS_AGGR_MIN) {
574
+ *target = val < *target ? val : *target;
575
+ } else if (aggregate == REDIS_AGGR_MAX) {
576
+ *target = val > *target ? val : *target;
577
+ } else {
578
+ /* safety net */
579
+ redisPanic("Unknown ZUNION/INTER aggregate type");
580
+ }
581
+ }
582
+
583
+ void zunionInterGenericCommand(redisClient *c, robj *dstkey, int op) {
584
+ int i, j, setnum;
585
+ int aggregate = REDIS_AGGR_SUM;
586
+ zsetopsrc *src;
587
+ robj *dstobj;
588
+ zset *dstzset;
589
+ zskiplistNode *znode;
590
+ dictIterator *di;
591
+ dictEntry *de;
592
+ int touched = 0;
593
+
594
+ /* expect setnum input keys to be given */
595
+ setnum = atoi(c->argv[2]->ptr);
596
+ if (setnum < 1) {
597
+ addReplyError(c,
598
+ "at least 1 input key is needed for ZUNIONSTORE/ZINTERSTORE");
599
+ return;
600
+ }
601
+
602
+ /* test if the expected number of keys would overflow */
603
+ if (3+setnum > c->argc) {
604
+ addReply(c,shared.syntaxerr);
605
+ return;
606
+ }
607
+
608
+ /* read keys to be used for input */
609
+ src = zmalloc(sizeof(zsetopsrc) * setnum);
610
+ for (i = 0, j = 3; i < setnum; i++, j++) {
611
+ robj *obj = lookupKeyWrite(c->db,c->argv[j]);
612
+ if (!obj) {
613
+ src[i].dict = NULL;
614
+ } else {
615
+ if (obj->type == REDIS_ZSET) {
616
+ src[i].dict = ((zset*)obj->ptr)->dict;
617
+ } else if (obj->type == REDIS_SET) {
618
+ if (obj->encoding == REDIS_ENCODING_INTSET)
619
+ setTypeConvert(obj, REDIS_ENCODING_HT);
620
+
621
+ redisAssert(obj->encoding == REDIS_ENCODING_HT);
622
+ src[i].dict = (obj->ptr);
623
+ } else {
624
+ zfree(src);
625
+ addReply(c,shared.wrongtypeerr);
626
+ return;
627
+ }
628
+ }
629
+
630
+ /* default all weights to 1 */
631
+ src[i].weight = 1.0;
632
+ }
633
+
634
+ /* parse optional extra arguments */
635
+ if (j < c->argc) {
636
+ int remaining = c->argc - j;
637
+
638
+ while (remaining) {
639
+ if (remaining >= (setnum + 1) && !strcasecmp(c->argv[j]->ptr,"weights")) {
640
+ j++; remaining--;
641
+ for (i = 0; i < setnum; i++, j++, remaining--) {
642
+ if (getDoubleFromObjectOrReply(c,c->argv[j],&src[i].weight,
643
+ "weight value is not a double") != REDIS_OK)
644
+ {
645
+ zfree(src);
646
+ return;
647
+ }
648
+ }
649
+ } else if (remaining >= 2 && !strcasecmp(c->argv[j]->ptr,"aggregate")) {
650
+ j++; remaining--;
651
+ if (!strcasecmp(c->argv[j]->ptr,"sum")) {
652
+ aggregate = REDIS_AGGR_SUM;
653
+ } else if (!strcasecmp(c->argv[j]->ptr,"min")) {
654
+ aggregate = REDIS_AGGR_MIN;
655
+ } else if (!strcasecmp(c->argv[j]->ptr,"max")) {
656
+ aggregate = REDIS_AGGR_MAX;
657
+ } else {
658
+ zfree(src);
659
+ addReply(c,shared.syntaxerr);
660
+ return;
661
+ }
662
+ j++; remaining--;
663
+ } else {
664
+ zfree(src);
665
+ addReply(c,shared.syntaxerr);
666
+ return;
667
+ }
668
+ }
669
+ }
670
+
671
+ /* sort sets from the smallest to largest, this will improve our
672
+ * algorithm's performance */
673
+ qsort(src,setnum,sizeof(zsetopsrc),qsortCompareZsetopsrcByCardinality);
674
+
675
+ dstobj = createZsetObject();
676
+ dstzset = dstobj->ptr;
677
+
678
+ if (op == REDIS_OP_INTER) {
679
+ /* skip going over all entries if the smallest zset is NULL or empty */
680
+ if (src[0].dict && dictSize(src[0].dict) > 0) {
681
+ /* precondition: as src[0].dict is non-empty and the zsets are ordered
682
+ * from small to large, all src[i > 0].dict are non-empty too */
683
+ di = dictGetIterator(src[0].dict);
684
+ while((de = dictNext(di)) != NULL) {
685
+ double score, value;
686
+
687
+ score = src[0].weight * zunionInterDictValue(de);
688
+ for (j = 1; j < setnum; j++) {
689
+ dictEntry *other;
690
+
691
+ /* If it's the same dictionary don't lookup as we are not
692
+ * in the context of a safe iterator. It's the same
693
+ * dictionary so we are sure the element is inside.
694
+ * This happens on SINTERSTORE dest 2 mykey mykey. */
695
+ if (src[j].dict == src[0].dict) {
696
+ other = de;
697
+ } else {
698
+ other = dictFind(src[j].dict,dictGetEntryKey(de));
699
+ }
700
+
701
+ if (other) {
702
+ value = src[j].weight * zunionInterDictValue(other);
703
+ zunionInterAggregate(&score,value,aggregate);
704
+ } else {
705
+ break;
706
+ }
707
+ }
708
+
709
+ /* Only continue when present in every source dict. */
710
+ if (j == setnum) {
711
+ robj *o = dictGetEntryKey(de);
712
+ znode = zslInsert(dstzset->zsl,score,o);
713
+ incrRefCount(o); /* added to skiplist */
714
+ dictAdd(dstzset->dict,o,&znode->score);
715
+ incrRefCount(o); /* added to dictionary */
716
+ }
717
+ }
718
+ dictReleaseIterator(di);
719
+ }
720
+ } else if (op == REDIS_OP_UNION) {
721
+ for (i = 0; i < setnum; i++) {
722
+ if (!src[i].dict) continue;
723
+
724
+ di = dictGetIterator(src[i].dict);
725
+ while((de = dictNext(di)) != NULL) {
726
+ double score, value;
727
+
728
+ /* skip key when already processed */
729
+ if (dictFind(dstzset->dict,dictGetEntryKey(de)) != NULL)
730
+ continue;
731
+
732
+ /* initialize score */
733
+ score = src[i].weight * zunionInterDictValue(de);
734
+
735
+ /* because the zsets are sorted by size, its only possible
736
+ * for sets at larger indices to hold this entry */
737
+ for (j = (i+1); j < setnum; j++) {
738
+ /* It is not safe to access the zset we are
739
+ * iterating, so explicitly check for equal object. */
740
+ if (src[j].dict == src[i].dict) {
741
+ value = src[i].weight * zunionInterDictValue(de);
742
+ zunionInterAggregate(&score,value,aggregate);
743
+ } else {
744
+ dictEntry *other;
745
+
746
+ other = dictFind(src[j].dict,dictGetEntryKey(de));
747
+ if (other) {
748
+ value = src[j].weight * zunionInterDictValue(other);
749
+ zunionInterAggregate(&score,value,aggregate);
750
+ }
751
+ }
752
+ }
753
+
754
+ robj *o = dictGetEntryKey(de);
755
+ znode = zslInsert(dstzset->zsl,score,o);
756
+ incrRefCount(o); /* added to skiplist */
757
+ dictAdd(dstzset->dict,o,&znode->score);
758
+ incrRefCount(o); /* added to dictionary */
759
+ }
760
+ dictReleaseIterator(di);
761
+ }
762
+ } else {
763
+ /* unknown operator */
764
+ redisAssert(op == REDIS_OP_INTER || op == REDIS_OP_UNION);
765
+ }
766
+
767
+ if (dbDelete(c->db,dstkey)) {
768
+ touchWatchedKey(c->db,dstkey);
769
+ touched = 1;
770
+ server.dirty++;
771
+ }
772
+ if (dstzset->zsl->length) {
773
+ dbAdd(c->db,dstkey,dstobj);
774
+ addReplyLongLong(c, dstzset->zsl->length);
775
+ if (!touched) touchWatchedKey(c->db,dstkey);
776
+ server.dirty++;
777
+ } else {
778
+ decrRefCount(dstobj);
779
+ addReply(c, shared.czero);
780
+ }
781
+ zfree(src);
782
+ }
783
+
784
+ void zunionstoreCommand(redisClient *c) {
785
+ zunionInterGenericCommand(c,c->argv[1], REDIS_OP_UNION);
786
+ }
787
+
788
+ void zinterstoreCommand(redisClient *c) {
789
+ zunionInterGenericCommand(c,c->argv[1], REDIS_OP_INTER);
790
+ }
791
+
792
+ void zrangeGenericCommand(redisClient *c, int reverse) {
793
+ robj *o;
794
+ long start;
795
+ long end;
796
+ int withscores = 0;
797
+ int llen;
798
+ int rangelen, j;
799
+ zset *zsetobj;
800
+ zskiplist *zsl;
801
+ zskiplistNode *ln;
802
+ robj *ele;
803
+
804
+ if ((getLongFromObjectOrReply(c, c->argv[2], &start, NULL) != REDIS_OK) ||
805
+ (getLongFromObjectOrReply(c, c->argv[3], &end, NULL) != REDIS_OK)) return;
806
+
807
+ if (c->argc == 5 && !strcasecmp(c->argv[4]->ptr,"withscores")) {
808
+ withscores = 1;
809
+ } else if (c->argc >= 5) {
810
+ addReply(c,shared.syntaxerr);
811
+ return;
812
+ }
813
+
814
+ if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL
815
+ || checkType(c,o,REDIS_ZSET)) return;
816
+ zsetobj = o->ptr;
817
+ zsl = zsetobj->zsl;
818
+ llen = zsl->length;
819
+
820
+ /* convert negative indexes */
821
+ if (start < 0) start = llen+start;
822
+ if (end < 0) end = llen+end;
823
+ if (start < 0) start = 0;
824
+
825
+ /* Invariant: start >= 0, so this test will be true when end < 0.
826
+ * The range is empty when start > end or start >= length. */
827
+ if (start > end || start >= llen) {
828
+ addReply(c,shared.emptymultibulk);
829
+ return;
830
+ }
831
+ if (end >= llen) end = llen-1;
832
+ rangelen = (end-start)+1;
833
+
834
+ /* check if starting point is trivial, before searching
835
+ * the element in log(N) time */
836
+ if (reverse) {
837
+ ln = start == 0 ? zsl->tail : zslGetElementByRank(zsl, llen-start);
838
+ } else {
839
+ ln = start == 0 ?
840
+ zsl->header->level[0].forward : zslGetElementByRank(zsl, start+1);
841
+ }
842
+
843
+ /* Return the result in form of a multi-bulk reply */
844
+ addReplyMultiBulkLen(c,withscores ? (rangelen*2) : rangelen);
845
+ for (j = 0; j < rangelen; j++) {
846
+ ele = ln->obj;
847
+ addReplyBulk(c,ele);
848
+ if (withscores)
849
+ addReplyDouble(c,ln->score);
850
+ ln = reverse ? ln->backward : ln->level[0].forward;
851
+ }
852
+ }
853
+
854
+ void zrangeCommand(redisClient *c) {
855
+ zrangeGenericCommand(c,0);
856
+ }
857
+
858
+ void zrevrangeCommand(redisClient *c) {
859
+ zrangeGenericCommand(c,1);
860
+ }
861
+
862
+ /* This command implements ZRANGEBYSCORE, ZREVRANGEBYSCORE and ZCOUNT.
863
+ * If "justcount", only the number of elements in the range is returned. */
864
+ void genericZrangebyscoreCommand(redisClient *c, int reverse, int justcount) {
865
+ zrangespec range;
866
+ robj *o, *emptyreply;
867
+ zset *zsetobj;
868
+ zskiplist *zsl;
869
+ zskiplistNode *ln;
870
+ int offset = 0, limit = -1;
871
+ int withscores = 0;
872
+ unsigned long rangelen = 0;
873
+ void *replylen = NULL;
874
+
875
+ /* Parse the range arguments. */
876
+ if (zslParseRange(c->argv[2],c->argv[3],&range) != REDIS_OK) {
877
+ addReplyError(c,"min or max is not a double");
878
+ return;
879
+ }
880
+
881
+ /* Parse optional extra arguments. Note that ZCOUNT will exactly have
882
+ * 4 arguments, so we'll never enter the following code path. */
883
+ if (c->argc > 4) {
884
+ int remaining = c->argc - 4;
885
+ int pos = 4;
886
+
887
+ while (remaining) {
888
+ if (remaining >= 1 && !strcasecmp(c->argv[pos]->ptr,"withscores")) {
889
+ pos++; remaining--;
890
+ withscores = 1;
891
+ } else if (remaining >= 3 && !strcasecmp(c->argv[pos]->ptr,"limit")) {
892
+ offset = atoi(c->argv[pos+1]->ptr);
893
+ limit = atoi(c->argv[pos+2]->ptr);
894
+ pos += 3; remaining -= 3;
895
+ } else {
896
+ addReply(c,shared.syntaxerr);
897
+ return;
898
+ }
899
+ }
900
+ }
901
+
902
+ /* Ok, lookup the key and get the range */
903
+ emptyreply = justcount ? shared.czero : shared.emptymultibulk;
904
+ if ((o = lookupKeyReadOrReply(c,c->argv[1],emptyreply)) == NULL ||
905
+ checkType(c,o,REDIS_ZSET)) return;
906
+ zsetobj = o->ptr;
907
+ zsl = zsetobj->zsl;
908
+
909
+ /* If reversed, assume the elements are sorted from high to low score. */
910
+ ln = zslFirstWithScore(zsl,range.min);
911
+ if (reverse) {
912
+ /* If range.min is out of range, ln will be NULL and we need to use
913
+ * the tail of the skiplist as first node of the range. */
914
+ if (ln == NULL) ln = zsl->tail;
915
+
916
+ /* zslFirstWithScore returns the first element with where with
917
+ * score >= range.min, so backtrack to make sure the element we use
918
+ * here has score <= range.min. */
919
+ while (ln && ln->score > range.min) ln = ln->backward;
920
+
921
+ /* Move to the right element according to the range spec. */
922
+ if (range.minex) {
923
+ /* Find last element with score < range.min */
924
+ while (ln && ln->score == range.min) ln = ln->backward;
925
+ } else {
926
+ /* Find last element with score <= range.min */
927
+ while (ln && ln->level[0].forward &&
928
+ ln->level[0].forward->score == range.min)
929
+ ln = ln->level[0].forward;
930
+ }
931
+ } else {
932
+ if (range.minex) {
933
+ /* Find first element with score > range.min */
934
+ while (ln && ln->score == range.min) ln = ln->level[0].forward;
935
+ }
936
+ }
937
+
938
+ /* No "first" element in the specified interval. */
939
+ if (ln == NULL) {
940
+ addReply(c,emptyreply);
941
+ return;
942
+ }
943
+
944
+ /* We don't know in advance how many matching elements there
945
+ * are in the list, so we push this object that will represent
946
+ * the multi-bulk length in the output buffer, and will "fix"
947
+ * it later */
948
+ if (!justcount)
949
+ replylen = addDeferredMultiBulkLength(c);
950
+
951
+ /* If there is an offset, just traverse the number of elements without
952
+ * checking the score because that is done in the next loop. */
953
+ while(ln && offset--) {
954
+ if (reverse)
955
+ ln = ln->backward;
956
+ else
957
+ ln = ln->level[0].forward;
958
+ }
959
+
960
+ while (ln && limit--) {
961
+ /* Check if this this element is in range. */
962
+ if (reverse) {
963
+ if (range.maxex) {
964
+ /* Element should have score > range.max */
965
+ if (ln->score <= range.max) break;
966
+ } else {
967
+ /* Element should have score >= range.max */
968
+ if (ln->score < range.max) break;
969
+ }
970
+ } else {
971
+ if (range.maxex) {
972
+ /* Element should have score < range.max */
973
+ if (ln->score >= range.max) break;
974
+ } else {
975
+ /* Element should have score <= range.max */
976
+ if (ln->score > range.max) break;
977
+ }
978
+ }
979
+
980
+ /* Do our magic */
981
+ rangelen++;
982
+ if (!justcount) {
983
+ addReplyBulk(c,ln->obj);
984
+ if (withscores)
985
+ addReplyDouble(c,ln->score);
986
+ }
987
+
988
+ if (reverse)
989
+ ln = ln->backward;
990
+ else
991
+ ln = ln->level[0].forward;
992
+ }
993
+
994
+ if (justcount) {
995
+ addReplyLongLong(c,(long)rangelen);
996
+ } else {
997
+ setDeferredMultiBulkLength(c,replylen,
998
+ withscores ? (rangelen*2) : rangelen);
999
+ }
1000
+ }
1001
+
1002
+ void zrangebyscoreCommand(redisClient *c) {
1003
+ genericZrangebyscoreCommand(c,0,0);
1004
+ }
1005
+
1006
+ void zrevrangebyscoreCommand(redisClient *c) {
1007
+ genericZrangebyscoreCommand(c,1,0);
1008
+ }
1009
+
1010
+ void zcountCommand(redisClient *c) {
1011
+ genericZrangebyscoreCommand(c,0,1);
1012
+ }
1013
+
1014
+ void zcardCommand(redisClient *c) {
1015
+ robj *o;
1016
+ zset *zs;
1017
+
1018
+ if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
1019
+ checkType(c,o,REDIS_ZSET)) return;
1020
+
1021
+ zs = o->ptr;
1022
+ addReplyLongLong(c,zs->zsl->length);
1023
+ }
1024
+
1025
+ void zscoreCommand(redisClient *c) {
1026
+ robj *o;
1027
+ zset *zs;
1028
+ dictEntry *de;
1029
+
1030
+ if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
1031
+ checkType(c,o,REDIS_ZSET)) return;
1032
+
1033
+ zs = o->ptr;
1034
+ c->argv[2] = tryObjectEncoding(c->argv[2]);
1035
+ de = dictFind(zs->dict,c->argv[2]);
1036
+ if (!de) {
1037
+ addReply(c,shared.nullbulk);
1038
+ } else {
1039
+ double *score = dictGetEntryVal(de);
1040
+
1041
+ addReplyDouble(c,*score);
1042
+ }
1043
+ }
1044
+
1045
+ void zrankGenericCommand(redisClient *c, int reverse) {
1046
+ robj *o;
1047
+ zset *zs;
1048
+ zskiplist *zsl;
1049
+ dictEntry *de;
1050
+ unsigned long rank;
1051
+ double *score;
1052
+
1053
+ if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
1054
+ checkType(c,o,REDIS_ZSET)) return;
1055
+
1056
+ zs = o->ptr;
1057
+ zsl = zs->zsl;
1058
+ c->argv[2] = tryObjectEncoding(c->argv[2]);
1059
+ de = dictFind(zs->dict,c->argv[2]);
1060
+ if (!de) {
1061
+ addReply(c,shared.nullbulk);
1062
+ return;
1063
+ }
1064
+
1065
+ score = dictGetEntryVal(de);
1066
+ rank = zslGetRank(zsl, *score, c->argv[2]);
1067
+ if (rank) {
1068
+ if (reverse) {
1069
+ addReplyLongLong(c, zsl->length - rank);
1070
+ } else {
1071
+ addReplyLongLong(c, rank-1);
1072
+ }
1073
+ } else {
1074
+ addReply(c,shared.nullbulk);
1075
+ }
1076
+ }
1077
+
1078
+ void zrankCommand(redisClient *c) {
1079
+ zrankGenericCommand(c, 0);
1080
+ }
1081
+
1082
+ void zrevrankCommand(redisClient *c) {
1083
+ zrankGenericCommand(c, 1);
1084
+ }