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,986 @@
1
+ #include "redis.h"
2
+
3
+ /*-----------------------------------------------------------------------------
4
+ * List API
5
+ *----------------------------------------------------------------------------*/
6
+
7
+ /* Check the argument length to see if it requires us to convert the ziplist
8
+ * to a real list. Only check raw-encoded objects because integer encoded
9
+ * objects are never too long. */
10
+ void listTypeTryConversion(robj *subject, robj *value) {
11
+ if (subject->encoding != REDIS_ENCODING_ZIPLIST) return;
12
+ if (value->encoding == REDIS_ENCODING_RAW &&
13
+ sdslen(value->ptr) > server.list_max_ziplist_value)
14
+ listTypeConvert(subject,REDIS_ENCODING_LINKEDLIST);
15
+ }
16
+
17
+ void listTypePush(robj *subject, robj *value, int where) {
18
+ /* Check if we need to convert the ziplist */
19
+ listTypeTryConversion(subject,value);
20
+ if (subject->encoding == REDIS_ENCODING_ZIPLIST &&
21
+ ziplistLen(subject->ptr) >= server.list_max_ziplist_entries)
22
+ listTypeConvert(subject,REDIS_ENCODING_LINKEDLIST);
23
+
24
+ if (subject->encoding == REDIS_ENCODING_ZIPLIST) {
25
+ int pos = (where == REDIS_HEAD) ? ZIPLIST_HEAD : ZIPLIST_TAIL;
26
+ value = getDecodedObject(value);
27
+ subject->ptr = ziplistPush(subject->ptr,value->ptr,sdslen(value->ptr),pos);
28
+ decrRefCount(value);
29
+ } else if (subject->encoding == REDIS_ENCODING_LINKEDLIST) {
30
+ if (where == REDIS_HEAD) {
31
+ listAddNodeHead(subject->ptr,value);
32
+ } else {
33
+ listAddNodeTail(subject->ptr,value);
34
+ }
35
+ incrRefCount(value);
36
+ } else {
37
+ redisPanic("Unknown list encoding");
38
+ }
39
+ }
40
+
41
+ robj *listTypePop(robj *subject, int where) {
42
+ robj *value = NULL;
43
+ if (subject->encoding == REDIS_ENCODING_ZIPLIST) {
44
+ unsigned char *p;
45
+ unsigned char *vstr;
46
+ unsigned int vlen;
47
+ long long vlong;
48
+ int pos = (where == REDIS_HEAD) ? 0 : -1;
49
+ p = ziplistIndex(subject->ptr,pos);
50
+ if (ziplistGet(p,&vstr,&vlen,&vlong)) {
51
+ if (vstr) {
52
+ value = createStringObject((char*)vstr,vlen);
53
+ } else {
54
+ value = createStringObjectFromLongLong(vlong);
55
+ }
56
+ /* We only need to delete an element when it exists */
57
+ subject->ptr = ziplistDelete(subject->ptr,&p);
58
+ }
59
+ } else if (subject->encoding == REDIS_ENCODING_LINKEDLIST) {
60
+ list *list = subject->ptr;
61
+ listNode *ln;
62
+ if (where == REDIS_HEAD) {
63
+ ln = listFirst(list);
64
+ } else {
65
+ ln = listLast(list);
66
+ }
67
+ if (ln != NULL) {
68
+ value = listNodeValue(ln);
69
+ incrRefCount(value);
70
+ listDelNode(list,ln);
71
+ }
72
+ } else {
73
+ redisPanic("Unknown list encoding");
74
+ }
75
+ return value;
76
+ }
77
+
78
+ unsigned long listTypeLength(robj *subject) {
79
+ if (subject->encoding == REDIS_ENCODING_ZIPLIST) {
80
+ return ziplistLen(subject->ptr);
81
+ } else if (subject->encoding == REDIS_ENCODING_LINKEDLIST) {
82
+ return listLength((list*)subject->ptr);
83
+ } else {
84
+ redisPanic("Unknown list encoding");
85
+ }
86
+ }
87
+
88
+ /* Initialize an iterator at the specified index. */
89
+ listTypeIterator *listTypeInitIterator(robj *subject, int index, unsigned char direction) {
90
+ listTypeIterator *li = zmalloc(sizeof(listTypeIterator));
91
+ li->subject = subject;
92
+ li->encoding = subject->encoding;
93
+ li->direction = direction;
94
+ if (li->encoding == REDIS_ENCODING_ZIPLIST) {
95
+ li->zi = ziplistIndex(subject->ptr,index);
96
+ } else if (li->encoding == REDIS_ENCODING_LINKEDLIST) {
97
+ li->ln = listIndex(subject->ptr,index);
98
+ } else {
99
+ redisPanic("Unknown list encoding");
100
+ }
101
+ return li;
102
+ }
103
+
104
+ /* Clean up the iterator. */
105
+ void listTypeReleaseIterator(listTypeIterator *li) {
106
+ zfree(li);
107
+ }
108
+
109
+ /* Stores pointer to current the entry in the provided entry structure
110
+ * and advances the position of the iterator. Returns 1 when the current
111
+ * entry is in fact an entry, 0 otherwise. */
112
+ int listTypeNext(listTypeIterator *li, listTypeEntry *entry) {
113
+ /* Protect from converting when iterating */
114
+ redisAssert(li->subject->encoding == li->encoding);
115
+
116
+ entry->li = li;
117
+ if (li->encoding == REDIS_ENCODING_ZIPLIST) {
118
+ entry->zi = li->zi;
119
+ if (entry->zi != NULL) {
120
+ if (li->direction == REDIS_TAIL)
121
+ li->zi = ziplistNext(li->subject->ptr,li->zi);
122
+ else
123
+ li->zi = ziplistPrev(li->subject->ptr,li->zi);
124
+ return 1;
125
+ }
126
+ } else if (li->encoding == REDIS_ENCODING_LINKEDLIST) {
127
+ entry->ln = li->ln;
128
+ if (entry->ln != NULL) {
129
+ if (li->direction == REDIS_TAIL)
130
+ li->ln = li->ln->next;
131
+ else
132
+ li->ln = li->ln->prev;
133
+ return 1;
134
+ }
135
+ } else {
136
+ redisPanic("Unknown list encoding");
137
+ }
138
+ return 0;
139
+ }
140
+
141
+ /* Return entry or NULL at the current position of the iterator. */
142
+ robj *listTypeGet(listTypeEntry *entry) {
143
+ listTypeIterator *li = entry->li;
144
+ robj *value = NULL;
145
+ if (li->encoding == REDIS_ENCODING_ZIPLIST) {
146
+ unsigned char *vstr;
147
+ unsigned int vlen;
148
+ long long vlong;
149
+ redisAssert(entry->zi != NULL);
150
+ if (ziplistGet(entry->zi,&vstr,&vlen,&vlong)) {
151
+ if (vstr) {
152
+ value = createStringObject((char*)vstr,vlen);
153
+ } else {
154
+ value = createStringObjectFromLongLong(vlong);
155
+ }
156
+ }
157
+ } else if (li->encoding == REDIS_ENCODING_LINKEDLIST) {
158
+ redisAssert(entry->ln != NULL);
159
+ value = listNodeValue(entry->ln);
160
+ incrRefCount(value);
161
+ } else {
162
+ redisPanic("Unknown list encoding");
163
+ }
164
+ return value;
165
+ }
166
+
167
+ void listTypeInsert(listTypeEntry *entry, robj *value, int where) {
168
+ robj *subject = entry->li->subject;
169
+ if (entry->li->encoding == REDIS_ENCODING_ZIPLIST) {
170
+ value = getDecodedObject(value);
171
+ if (where == REDIS_TAIL) {
172
+ unsigned char *next = ziplistNext(subject->ptr,entry->zi);
173
+
174
+ /* When we insert after the current element, but the current element
175
+ * is the tail of the list, we need to do a push. */
176
+ if (next == NULL) {
177
+ subject->ptr = ziplistPush(subject->ptr,value->ptr,sdslen(value->ptr),REDIS_TAIL);
178
+ } else {
179
+ subject->ptr = ziplistInsert(subject->ptr,next,value->ptr,sdslen(value->ptr));
180
+ }
181
+ } else {
182
+ subject->ptr = ziplistInsert(subject->ptr,entry->zi,value->ptr,sdslen(value->ptr));
183
+ }
184
+ decrRefCount(value);
185
+ } else if (entry->li->encoding == REDIS_ENCODING_LINKEDLIST) {
186
+ if (where == REDIS_TAIL) {
187
+ listInsertNode(subject->ptr,entry->ln,value,AL_START_TAIL);
188
+ } else {
189
+ listInsertNode(subject->ptr,entry->ln,value,AL_START_HEAD);
190
+ }
191
+ incrRefCount(value);
192
+ } else {
193
+ redisPanic("Unknown list encoding");
194
+ }
195
+ }
196
+
197
+ /* Compare the given object with the entry at the current position. */
198
+ int listTypeEqual(listTypeEntry *entry, robj *o) {
199
+ listTypeIterator *li = entry->li;
200
+ if (li->encoding == REDIS_ENCODING_ZIPLIST) {
201
+ redisAssert(o->encoding == REDIS_ENCODING_RAW);
202
+ return ziplistCompare(entry->zi,o->ptr,sdslen(o->ptr));
203
+ } else if (li->encoding == REDIS_ENCODING_LINKEDLIST) {
204
+ return equalStringObjects(o,listNodeValue(entry->ln));
205
+ } else {
206
+ redisPanic("Unknown list encoding");
207
+ }
208
+ }
209
+
210
+ /* Delete the element pointed to. */
211
+ void listTypeDelete(listTypeEntry *entry) {
212
+ listTypeIterator *li = entry->li;
213
+ if (li->encoding == REDIS_ENCODING_ZIPLIST) {
214
+ unsigned char *p = entry->zi;
215
+ li->subject->ptr = ziplistDelete(li->subject->ptr,&p);
216
+
217
+ /* Update position of the iterator depending on the direction */
218
+ if (li->direction == REDIS_TAIL)
219
+ li->zi = p;
220
+ else
221
+ li->zi = ziplistPrev(li->subject->ptr,p);
222
+ } else if (entry->li->encoding == REDIS_ENCODING_LINKEDLIST) {
223
+ listNode *next;
224
+ if (li->direction == REDIS_TAIL)
225
+ next = entry->ln->next;
226
+ else
227
+ next = entry->ln->prev;
228
+ listDelNode(li->subject->ptr,entry->ln);
229
+ li->ln = next;
230
+ } else {
231
+ redisPanic("Unknown list encoding");
232
+ }
233
+ }
234
+
235
+ void listTypeConvert(robj *subject, int enc) {
236
+ listTypeIterator *li;
237
+ listTypeEntry entry;
238
+ redisAssert(subject->type == REDIS_LIST);
239
+
240
+ if (enc == REDIS_ENCODING_LINKEDLIST) {
241
+ list *l = listCreate();
242
+ listSetFreeMethod(l,decrRefCount);
243
+
244
+ /* listTypeGet returns a robj with incremented refcount */
245
+ li = listTypeInitIterator(subject,0,REDIS_TAIL);
246
+ while (listTypeNext(li,&entry)) listAddNodeTail(l,listTypeGet(&entry));
247
+ listTypeReleaseIterator(li);
248
+
249
+ subject->encoding = REDIS_ENCODING_LINKEDLIST;
250
+ zfree(subject->ptr);
251
+ subject->ptr = l;
252
+ } else {
253
+ redisPanic("Unsupported list conversion");
254
+ }
255
+ }
256
+
257
+ /*-----------------------------------------------------------------------------
258
+ * List Commands
259
+ *----------------------------------------------------------------------------*/
260
+
261
+ void pushGenericCommand(redisClient *c, int where) {
262
+ robj *lobj = lookupKeyWrite(c->db,c->argv[1]);
263
+ c->argv[2] = tryObjectEncoding(c->argv[2]);
264
+ if (lobj == NULL) {
265
+ if (handleClientsWaitingListPush(c,c->argv[1],c->argv[2])) {
266
+ addReply(c,shared.cone);
267
+ return;
268
+ }
269
+ lobj = createZiplistObject();
270
+ dbAdd(c->db,c->argv[1],lobj);
271
+ } else {
272
+ if (lobj->type != REDIS_LIST) {
273
+ addReply(c,shared.wrongtypeerr);
274
+ return;
275
+ }
276
+ if (handleClientsWaitingListPush(c,c->argv[1],c->argv[2])) {
277
+ touchWatchedKey(c->db,c->argv[1]);
278
+ addReply(c,shared.cone);
279
+ return;
280
+ }
281
+ }
282
+ listTypePush(lobj,c->argv[2],where);
283
+ addReplyLongLong(c,listTypeLength(lobj));
284
+ touchWatchedKey(c->db,c->argv[1]);
285
+ server.dirty++;
286
+ }
287
+
288
+ void lpushCommand(redisClient *c) {
289
+ pushGenericCommand(c,REDIS_HEAD);
290
+ }
291
+
292
+ void rpushCommand(redisClient *c) {
293
+ pushGenericCommand(c,REDIS_TAIL);
294
+ }
295
+
296
+ void pushxGenericCommand(redisClient *c, robj *refval, robj *val, int where) {
297
+ robj *subject;
298
+ listTypeIterator *iter;
299
+ listTypeEntry entry;
300
+ int inserted = 0;
301
+
302
+ if ((subject = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
303
+ checkType(c,subject,REDIS_LIST)) return;
304
+
305
+ if (refval != NULL) {
306
+ /* Note: we expect refval to be string-encoded because it is *not* the
307
+ * last argument of the multi-bulk LINSERT. */
308
+ redisAssert(refval->encoding == REDIS_ENCODING_RAW);
309
+
310
+ /* We're not sure if this value can be inserted yet, but we cannot
311
+ * convert the list inside the iterator. We don't want to loop over
312
+ * the list twice (once to see if the value can be inserted and once
313
+ * to do the actual insert), so we assume this value can be inserted
314
+ * and convert the ziplist to a regular list if necessary. */
315
+ listTypeTryConversion(subject,val);
316
+
317
+ /* Seek refval from head to tail */
318
+ iter = listTypeInitIterator(subject,0,REDIS_TAIL);
319
+ while (listTypeNext(iter,&entry)) {
320
+ if (listTypeEqual(&entry,refval)) {
321
+ listTypeInsert(&entry,val,where);
322
+ inserted = 1;
323
+ break;
324
+ }
325
+ }
326
+ listTypeReleaseIterator(iter);
327
+
328
+ if (inserted) {
329
+ /* Check if the length exceeds the ziplist length threshold. */
330
+ if (subject->encoding == REDIS_ENCODING_ZIPLIST &&
331
+ ziplistLen(subject->ptr) > server.list_max_ziplist_entries)
332
+ listTypeConvert(subject,REDIS_ENCODING_LINKEDLIST);
333
+ touchWatchedKey(c->db,c->argv[1]);
334
+ server.dirty++;
335
+ } else {
336
+ /* Notify client of a failed insert */
337
+ addReply(c,shared.cnegone);
338
+ return;
339
+ }
340
+ } else {
341
+ listTypePush(subject,val,where);
342
+ touchWatchedKey(c->db,c->argv[1]);
343
+ server.dirty++;
344
+ }
345
+
346
+ addReplyLongLong(c,listTypeLength(subject));
347
+ }
348
+
349
+ void lpushxCommand(redisClient *c) {
350
+ c->argv[2] = tryObjectEncoding(c->argv[2]);
351
+ pushxGenericCommand(c,NULL,c->argv[2],REDIS_HEAD);
352
+ }
353
+
354
+ void rpushxCommand(redisClient *c) {
355
+ c->argv[2] = tryObjectEncoding(c->argv[2]);
356
+ pushxGenericCommand(c,NULL,c->argv[2],REDIS_TAIL);
357
+ }
358
+
359
+ void linsertCommand(redisClient *c) {
360
+ c->argv[4] = tryObjectEncoding(c->argv[4]);
361
+ if (strcasecmp(c->argv[2]->ptr,"after") == 0) {
362
+ pushxGenericCommand(c,c->argv[3],c->argv[4],REDIS_TAIL);
363
+ } else if (strcasecmp(c->argv[2]->ptr,"before") == 0) {
364
+ pushxGenericCommand(c,c->argv[3],c->argv[4],REDIS_HEAD);
365
+ } else {
366
+ addReply(c,shared.syntaxerr);
367
+ }
368
+ }
369
+
370
+ void llenCommand(redisClient *c) {
371
+ robj *o = lookupKeyReadOrReply(c,c->argv[1],shared.czero);
372
+ if (o == NULL || checkType(c,o,REDIS_LIST)) return;
373
+ addReplyLongLong(c,listTypeLength(o));
374
+ }
375
+
376
+ void lindexCommand(redisClient *c) {
377
+ robj *o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk);
378
+ if (o == NULL || checkType(c,o,REDIS_LIST)) return;
379
+ int index = atoi(c->argv[2]->ptr);
380
+ robj *value = NULL;
381
+
382
+ if (o->encoding == REDIS_ENCODING_ZIPLIST) {
383
+ unsigned char *p;
384
+ unsigned char *vstr;
385
+ unsigned int vlen;
386
+ long long vlong;
387
+ p = ziplistIndex(o->ptr,index);
388
+ if (ziplistGet(p,&vstr,&vlen,&vlong)) {
389
+ if (vstr) {
390
+ value = createStringObject((char*)vstr,vlen);
391
+ } else {
392
+ value = createStringObjectFromLongLong(vlong);
393
+ }
394
+ addReplyBulk(c,value);
395
+ decrRefCount(value);
396
+ } else {
397
+ addReply(c,shared.nullbulk);
398
+ }
399
+ } else if (o->encoding == REDIS_ENCODING_LINKEDLIST) {
400
+ listNode *ln = listIndex(o->ptr,index);
401
+ if (ln != NULL) {
402
+ value = listNodeValue(ln);
403
+ addReplyBulk(c,value);
404
+ } else {
405
+ addReply(c,shared.nullbulk);
406
+ }
407
+ } else {
408
+ redisPanic("Unknown list encoding");
409
+ }
410
+ }
411
+
412
+ void lsetCommand(redisClient *c) {
413
+ robj *o = lookupKeyWriteOrReply(c,c->argv[1],shared.nokeyerr);
414
+ if (o == NULL || checkType(c,o,REDIS_LIST)) return;
415
+ int index = atoi(c->argv[2]->ptr);
416
+ robj *value = (c->argv[3] = tryObjectEncoding(c->argv[3]));
417
+
418
+ listTypeTryConversion(o,value);
419
+ if (o->encoding == REDIS_ENCODING_ZIPLIST) {
420
+ unsigned char *p, *zl = o->ptr;
421
+ p = ziplistIndex(zl,index);
422
+ if (p == NULL) {
423
+ addReply(c,shared.outofrangeerr);
424
+ } else {
425
+ o->ptr = ziplistDelete(o->ptr,&p);
426
+ value = getDecodedObject(value);
427
+ o->ptr = ziplistInsert(o->ptr,p,value->ptr,sdslen(value->ptr));
428
+ decrRefCount(value);
429
+ addReply(c,shared.ok);
430
+ touchWatchedKey(c->db,c->argv[1]);
431
+ server.dirty++;
432
+ }
433
+ } else if (o->encoding == REDIS_ENCODING_LINKEDLIST) {
434
+ listNode *ln = listIndex(o->ptr,index);
435
+ if (ln == NULL) {
436
+ addReply(c,shared.outofrangeerr);
437
+ } else {
438
+ decrRefCount((robj*)listNodeValue(ln));
439
+ listNodeValue(ln) = value;
440
+ incrRefCount(value);
441
+ addReply(c,shared.ok);
442
+ touchWatchedKey(c->db,c->argv[1]);
443
+ server.dirty++;
444
+ }
445
+ } else {
446
+ redisPanic("Unknown list encoding");
447
+ }
448
+ }
449
+
450
+ void popGenericCommand(redisClient *c, int where) {
451
+ robj *o = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk);
452
+ if (o == NULL || checkType(c,o,REDIS_LIST)) return;
453
+
454
+ robj *value = listTypePop(o,where);
455
+ if (value == NULL) {
456
+ addReply(c,shared.nullbulk);
457
+ } else {
458
+ addReplyBulk(c,value);
459
+ decrRefCount(value);
460
+ if (listTypeLength(o) == 0) dbDelete(c->db,c->argv[1]);
461
+ touchWatchedKey(c->db,c->argv[1]);
462
+ server.dirty++;
463
+ }
464
+ }
465
+
466
+ void lpopCommand(redisClient *c) {
467
+ popGenericCommand(c,REDIS_HEAD);
468
+ }
469
+
470
+ void rpopCommand(redisClient *c) {
471
+ popGenericCommand(c,REDIS_TAIL);
472
+ }
473
+
474
+ void lrangeCommand(redisClient *c) {
475
+ robj *o;
476
+ int start = atoi(c->argv[2]->ptr);
477
+ int end = atoi(c->argv[3]->ptr);
478
+ int llen;
479
+ int rangelen;
480
+
481
+ if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL
482
+ || checkType(c,o,REDIS_LIST)) return;
483
+ llen = listTypeLength(o);
484
+
485
+ /* convert negative indexes */
486
+ if (start < 0) start = llen+start;
487
+ if (end < 0) end = llen+end;
488
+ if (start < 0) start = 0;
489
+
490
+ /* Invariant: start >= 0, so this test will be true when end < 0.
491
+ * The range is empty when start > end or start >= length. */
492
+ if (start > end || start >= llen) {
493
+ addReply(c,shared.emptymultibulk);
494
+ return;
495
+ }
496
+ if (end >= llen) end = llen-1;
497
+ rangelen = (end-start)+1;
498
+
499
+ /* Return the result in form of a multi-bulk reply */
500
+ addReplyMultiBulkLen(c,rangelen);
501
+ if (o->encoding == REDIS_ENCODING_ZIPLIST) {
502
+ unsigned char *p = ziplistIndex(o->ptr,start);
503
+ unsigned char *vstr;
504
+ unsigned int vlen;
505
+ long long vlong;
506
+
507
+ while(rangelen--) {
508
+ ziplistGet(p,&vstr,&vlen,&vlong);
509
+ if (vstr) {
510
+ addReplyBulkCBuffer(c,vstr,vlen);
511
+ } else {
512
+ addReplyBulkLongLong(c,vlong);
513
+ }
514
+ p = ziplistNext(o->ptr,p);
515
+ }
516
+ } else if (o->encoding == REDIS_ENCODING_LINKEDLIST) {
517
+ listNode *ln = listIndex(o->ptr,start);
518
+
519
+ while(rangelen--) {
520
+ addReplyBulk(c,ln->value);
521
+ ln = ln->next;
522
+ }
523
+ } else {
524
+ redisPanic("List encoding is not LINKEDLIST nor ZIPLIST!");
525
+ }
526
+ }
527
+
528
+ void ltrimCommand(redisClient *c) {
529
+ robj *o;
530
+ int start = atoi(c->argv[2]->ptr);
531
+ int end = atoi(c->argv[3]->ptr);
532
+ int llen;
533
+ int j, ltrim, rtrim;
534
+ list *list;
535
+ listNode *ln;
536
+
537
+ if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.ok)) == NULL ||
538
+ checkType(c,o,REDIS_LIST)) return;
539
+ llen = listTypeLength(o);
540
+
541
+ /* convert negative indexes */
542
+ if (start < 0) start = llen+start;
543
+ if (end < 0) end = llen+end;
544
+ if (start < 0) start = 0;
545
+
546
+ /* Invariant: start >= 0, so this test will be true when end < 0.
547
+ * The range is empty when start > end or start >= length. */
548
+ if (start > end || start >= llen) {
549
+ /* Out of range start or start > end result in empty list */
550
+ ltrim = llen;
551
+ rtrim = 0;
552
+ } else {
553
+ if (end >= llen) end = llen-1;
554
+ ltrim = start;
555
+ rtrim = llen-end-1;
556
+ }
557
+
558
+ /* Remove list elements to perform the trim */
559
+ if (o->encoding == REDIS_ENCODING_ZIPLIST) {
560
+ o->ptr = ziplistDeleteRange(o->ptr,0,ltrim);
561
+ o->ptr = ziplistDeleteRange(o->ptr,-rtrim,rtrim);
562
+ } else if (o->encoding == REDIS_ENCODING_LINKEDLIST) {
563
+ list = o->ptr;
564
+ for (j = 0; j < ltrim; j++) {
565
+ ln = listFirst(list);
566
+ listDelNode(list,ln);
567
+ }
568
+ for (j = 0; j < rtrim; j++) {
569
+ ln = listLast(list);
570
+ listDelNode(list,ln);
571
+ }
572
+ } else {
573
+ redisPanic("Unknown list encoding");
574
+ }
575
+ if (listTypeLength(o) == 0) dbDelete(c->db,c->argv[1]);
576
+ touchWatchedKey(c->db,c->argv[1]);
577
+ server.dirty++;
578
+ addReply(c,shared.ok);
579
+ }
580
+
581
+ void lremCommand(redisClient *c) {
582
+ robj *subject, *obj;
583
+ obj = c->argv[3] = tryObjectEncoding(c->argv[3]);
584
+ int toremove = atoi(c->argv[2]->ptr);
585
+ int removed = 0;
586
+ listTypeEntry entry;
587
+
588
+ subject = lookupKeyWriteOrReply(c,c->argv[1],shared.czero);
589
+ if (subject == NULL || checkType(c,subject,REDIS_LIST)) return;
590
+
591
+ /* Make sure obj is raw when we're dealing with a ziplist */
592
+ if (subject->encoding == REDIS_ENCODING_ZIPLIST)
593
+ obj = getDecodedObject(obj);
594
+
595
+ listTypeIterator *li;
596
+ if (toremove < 0) {
597
+ toremove = -toremove;
598
+ li = listTypeInitIterator(subject,-1,REDIS_HEAD);
599
+ } else {
600
+ li = listTypeInitIterator(subject,0,REDIS_TAIL);
601
+ }
602
+
603
+ while (listTypeNext(li,&entry)) {
604
+ if (listTypeEqual(&entry,obj)) {
605
+ listTypeDelete(&entry);
606
+ server.dirty++;
607
+ removed++;
608
+ if (toremove && removed == toremove) break;
609
+ }
610
+ }
611
+ listTypeReleaseIterator(li);
612
+
613
+ /* Clean up raw encoded object */
614
+ if (subject->encoding == REDIS_ENCODING_ZIPLIST)
615
+ decrRefCount(obj);
616
+
617
+ if (listTypeLength(subject) == 0) dbDelete(c->db,c->argv[1]);
618
+ addReplyLongLong(c,removed);
619
+ if (removed) touchWatchedKey(c->db,c->argv[1]);
620
+ }
621
+
622
+ /* This is the semantic of this command:
623
+ * RPOPLPUSH srclist dstlist:
624
+ * IF LLEN(srclist) > 0
625
+ * element = RPOP srclist
626
+ * LPUSH dstlist element
627
+ * RETURN element
628
+ * ELSE
629
+ * RETURN nil
630
+ * END
631
+ * END
632
+ *
633
+ * The idea is to be able to get an element from a list in a reliable way
634
+ * since the element is not just returned but pushed against another list
635
+ * as well. This command was originally proposed by Ezra Zygmuntowicz.
636
+ */
637
+
638
+ void rpoplpushHandlePush(redisClient *origclient, redisClient *c, robj *dstkey, robj *dstobj, robj *value) {
639
+ robj *aux;
640
+
641
+ if (!handleClientsWaitingListPush(c,dstkey,value)) {
642
+ /* Create the list if the key does not exist */
643
+ if (!dstobj) {
644
+ dstobj = createZiplistObject();
645
+ dbAdd(c->db,dstkey,dstobj);
646
+ } else {
647
+ touchWatchedKey(c->db,dstkey);
648
+ }
649
+ listTypePush(dstobj,value,REDIS_HEAD);
650
+ /* If we are pushing as a result of LPUSH against a key
651
+ * watched by BLPOPLPUSH, we need to rewrite the command vector.
652
+ * But if this is called directly by RPOPLPUSH (either directly
653
+ * or via a BRPOPLPUSH where the popped list exists)
654
+ * we should replicate the BRPOPLPUSH command itself. */
655
+ if (c != origclient) {
656
+ aux = createStringObject("LPUSH",5);
657
+ rewriteClientCommandVector(origclient,3,aux,dstkey,value);
658
+ decrRefCount(aux);
659
+ } else {
660
+ /* Make sure to always use RPOPLPUSH in the replication / AOF,
661
+ * even if the original command was BRPOPLPUSH. */
662
+ aux = createStringObject("RPOPLPUSH",9);
663
+ rewriteClientCommandVector(origclient,3,aux,c->argv[1],c->argv[2]);
664
+ decrRefCount(aux);
665
+ }
666
+ server.dirty++;
667
+ }
668
+
669
+ /* Always send the pushed value to the client. */
670
+ addReplyBulk(c,value);
671
+ }
672
+
673
+ void rpoplpushCommand(redisClient *c) {
674
+ robj *sobj, *value;
675
+ if ((sobj = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
676
+ checkType(c,sobj,REDIS_LIST)) return;
677
+
678
+ if (listTypeLength(sobj) == 0) {
679
+ addReply(c,shared.nullbulk);
680
+ } else {
681
+ robj *dobj = lookupKeyWrite(c->db,c->argv[2]);
682
+ robj *touchedkey = c->argv[1];
683
+
684
+ if (dobj && checkType(c,dobj,REDIS_LIST)) return;
685
+ value = listTypePop(sobj,REDIS_TAIL);
686
+ /* We saved touched key, and protect it, since rpoplpushHandlePush
687
+ * may change the client command argument vector. */
688
+ incrRefCount(touchedkey);
689
+ rpoplpushHandlePush(c,c,c->argv[2],dobj,value);
690
+
691
+ /* listTypePop returns an object with its refcount incremented */
692
+ decrRefCount(value);
693
+
694
+ /* Delete the source list when it is empty */
695
+ if (listTypeLength(sobj) == 0) dbDelete(c->db,touchedkey);
696
+ touchWatchedKey(c->db,touchedkey);
697
+ decrRefCount(touchedkey);
698
+ server.dirty++;
699
+ }
700
+ }
701
+
702
+ /*-----------------------------------------------------------------------------
703
+ * Blocking POP operations
704
+ *----------------------------------------------------------------------------*/
705
+
706
+ /* Currently Redis blocking operations support is limited to list POP ops,
707
+ * so the current implementation is not fully generic, but it is also not
708
+ * completely specific so it will not require a rewrite to support new
709
+ * kind of blocking operations in the future.
710
+ *
711
+ * Still it's important to note that list blocking operations can be already
712
+ * used as a notification mechanism in order to implement other blocking
713
+ * operations at application level, so there must be a very strong evidence
714
+ * of usefulness and generality before new blocking operations are implemented.
715
+ *
716
+ * This is how the current blocking POP works, we use BLPOP as example:
717
+ * - If the user calls BLPOP and the key exists and contains a non empty list
718
+ * then LPOP is called instead. So BLPOP is semantically the same as LPOP
719
+ * if there is not to block.
720
+ * - If instead BLPOP is called and the key does not exists or the list is
721
+ * empty we need to block. In order to do so we remove the notification for
722
+ * new data to read in the client socket (so that we'll not serve new
723
+ * requests if the blocking request is not served). Also we put the client
724
+ * in a dictionary (db->blocking_keys) mapping keys to a list of clients
725
+ * blocking for this keys.
726
+ * - If a PUSH operation against a key with blocked clients waiting is
727
+ * performed, we serve the first in the list: basically instead to push
728
+ * the new element inside the list we return it to the (first / oldest)
729
+ * blocking client, unblock the client, and remove it form the list.
730
+ *
731
+ * The above comment and the source code should be enough in order to understand
732
+ * the implementation and modify / fix it later.
733
+ */
734
+
735
+ /* Set a client in blocking mode for the specified key, with the specified
736
+ * timeout */
737
+ void blockForKeys(redisClient *c, robj **keys, int numkeys, time_t timeout, robj *target) {
738
+ dictEntry *de;
739
+ list *l;
740
+ int j;
741
+
742
+ c->bpop.keys = zmalloc(sizeof(robj*)*numkeys);
743
+ c->bpop.count = numkeys;
744
+ c->bpop.timeout = timeout;
745
+ c->bpop.target = target;
746
+
747
+ if (target != NULL) {
748
+ incrRefCount(target);
749
+ }
750
+
751
+ for (j = 0; j < numkeys; j++) {
752
+ /* Add the key in the client structure, to map clients -> keys */
753
+ c->bpop.keys[j] = keys[j];
754
+ incrRefCount(keys[j]);
755
+
756
+ /* And in the other "side", to map keys -> clients */
757
+ de = dictFind(c->db->blocking_keys,keys[j]);
758
+ if (de == NULL) {
759
+ int retval;
760
+
761
+ /* For every key we take a list of clients blocked for it */
762
+ l = listCreate();
763
+ retval = dictAdd(c->db->blocking_keys,keys[j],l);
764
+ incrRefCount(keys[j]);
765
+ redisAssert(retval == DICT_OK);
766
+ } else {
767
+ l = dictGetEntryVal(de);
768
+ }
769
+ listAddNodeTail(l,c);
770
+ }
771
+ /* Mark the client as a blocked client */
772
+ c->flags |= REDIS_BLOCKED;
773
+ server.bpop_blocked_clients++;
774
+ }
775
+
776
+ /* Unblock a client that's waiting in a blocking operation such as BLPOP */
777
+ void unblockClientWaitingData(redisClient *c) {
778
+ dictEntry *de;
779
+ list *l;
780
+ int j;
781
+
782
+ redisAssert(c->bpop.keys != NULL);
783
+ /* The client may wait for multiple keys, so unblock it for every key. */
784
+ for (j = 0; j < c->bpop.count; j++) {
785
+ /* Remove this client from the list of clients waiting for this key. */
786
+ de = dictFind(c->db->blocking_keys,c->bpop.keys[j]);
787
+ redisAssert(de != NULL);
788
+ l = dictGetEntryVal(de);
789
+ listDelNode(l,listSearchKey(l,c));
790
+ /* If the list is empty we need to remove it to avoid wasting memory */
791
+ if (listLength(l) == 0)
792
+ dictDelete(c->db->blocking_keys,c->bpop.keys[j]);
793
+ decrRefCount(c->bpop.keys[j]);
794
+ }
795
+
796
+ /* Cleanup the client structure */
797
+ zfree(c->bpop.keys);
798
+ c->bpop.keys = NULL;
799
+ if (c->bpop.target) decrRefCount(c->bpop.target);
800
+ c->bpop.target = NULL;
801
+ c->flags &= ~REDIS_BLOCKED;
802
+ c->flags |= REDIS_UNBLOCKED;
803
+ server.bpop_blocked_clients--;
804
+ listAddNodeTail(server.unblocked_clients,c);
805
+ }
806
+
807
+ /* This should be called from any function PUSHing into lists.
808
+ * 'c' is the "pushing client", 'key' is the key it is pushing data against,
809
+ * 'ele' is the element pushed.
810
+ *
811
+ * If the function returns 0 there was no client waiting for a list push
812
+ * against this key.
813
+ *
814
+ * If the function returns 1 there was a client waiting for a list push
815
+ * against this key, the element was passed to this client thus it's not
816
+ * needed to actually add it to the list and the caller should return asap. */
817
+ int handleClientsWaitingListPush(redisClient *c, robj *key, robj *ele) {
818
+ struct dictEntry *de;
819
+ redisClient *receiver;
820
+ int numclients;
821
+ list *clients;
822
+ listNode *ln;
823
+ robj *dstkey, *dstobj;
824
+
825
+ de = dictFind(c->db->blocking_keys,key);
826
+ if (de == NULL) return 0;
827
+ clients = dictGetEntryVal(de);
828
+ numclients = listLength(clients);
829
+
830
+ /* Try to handle the push as long as there are clients waiting for a push.
831
+ * Note that "numclients" is used because the list of clients waiting for a
832
+ * push on "key" is deleted by unblockClient() when empty.
833
+ *
834
+ * This loop will have more than 1 iteration when there is a BRPOPLPUSH
835
+ * that cannot push the target list because it does not contain a list. If
836
+ * this happens, it simply tries the next client waiting for a push. */
837
+ while (numclients--) {
838
+ ln = listFirst(clients);
839
+ redisAssert(ln != NULL);
840
+ receiver = ln->value;
841
+ dstkey = receiver->bpop.target;
842
+
843
+ /* Protect receiver->bpop.target, that will be freed by
844
+ * the next unblockClientWaitingData() call. */
845
+ if (dstkey) incrRefCount(dstkey);
846
+ /* This should remove the first element of the "clients" list. */
847
+ unblockClientWaitingData(receiver);
848
+
849
+ if (dstkey == NULL) {
850
+ /* BRPOP/BLPOP */
851
+ addReplyMultiBulkLen(receiver,2);
852
+ addReplyBulk(receiver,key);
853
+ addReplyBulk(receiver,ele);
854
+ return 1; /* Serve just the first client as in B[RL]POP semantics */
855
+ } else {
856
+ /* BRPOPLPUSH, note that receiver->db is always equal to c->db. */
857
+ dstobj = lookupKeyWrite(receiver->db,dstkey);
858
+ if (!(dstobj && checkType(receiver,dstobj,REDIS_LIST))) {
859
+ rpoplpushHandlePush(c,receiver,dstkey,dstobj,ele);
860
+ decrRefCount(dstkey);
861
+ return 1;
862
+ }
863
+ decrRefCount(dstkey);
864
+ }
865
+ }
866
+
867
+ return 0;
868
+ }
869
+
870
+ int getTimeoutFromObjectOrReply(redisClient *c, robj *object, time_t *timeout) {
871
+ long tval;
872
+
873
+ if (getLongFromObjectOrReply(c,object,&tval,
874
+ "timeout is not an integer or out of range") != REDIS_OK)
875
+ return REDIS_ERR;
876
+
877
+ if (tval < 0) {
878
+ addReplyError(c,"timeout is negative");
879
+ return REDIS_ERR;
880
+ }
881
+
882
+ if (tval > 0) tval += time(NULL);
883
+ *timeout = tval;
884
+
885
+ return REDIS_OK;
886
+ }
887
+
888
+ /* Blocking RPOP/LPOP */
889
+ void blockingPopGenericCommand(redisClient *c, int where) {
890
+ robj *o;
891
+ time_t timeout;
892
+ int j;
893
+
894
+ if (getTimeoutFromObjectOrReply(c,c->argv[c->argc-1],&timeout) != REDIS_OK)
895
+ return;
896
+
897
+ for (j = 1; j < c->argc-1; j++) {
898
+ o = lookupKeyWrite(c->db,c->argv[j]);
899
+ if (o != NULL) {
900
+ if (o->type != REDIS_LIST) {
901
+ addReply(c,shared.wrongtypeerr);
902
+ return;
903
+ } else {
904
+ if (listTypeLength(o) != 0) {
905
+ /* If the list contains elements fall back to the usual
906
+ * non-blocking POP operation */
907
+ robj *argv[2], **orig_argv;
908
+ int orig_argc;
909
+
910
+ /* We need to alter the command arguments before to call
911
+ * popGenericCommand() as the command takes a single key. */
912
+ orig_argv = c->argv;
913
+ orig_argc = c->argc;
914
+ argv[1] = c->argv[j];
915
+ c->argv = argv;
916
+ c->argc = 2;
917
+
918
+ /* Also the return value is different, we need to output
919
+ * the multi bulk reply header and the key name. The
920
+ * "real" command will add the last element (the value)
921
+ * for us. If this souds like an hack to you it's just
922
+ * because it is... */
923
+ addReplyMultiBulkLen(c,2);
924
+ addReplyBulk(c,argv[1]);
925
+
926
+ popGenericCommand(c,where);
927
+
928
+ /* Fix the client structure with the original stuff */
929
+ c->argv = orig_argv;
930
+ c->argc = orig_argc;
931
+
932
+ return;
933
+ }
934
+ }
935
+ }
936
+ }
937
+
938
+ /* If we are inside a MULTI/EXEC and the list is empty the only thing
939
+ * we can do is treating it as a timeout (even with timeout 0). */
940
+ if (c->flags & REDIS_MULTI) {
941
+ addReply(c,shared.nullmultibulk);
942
+ return;
943
+ }
944
+
945
+ /* If the list is empty or the key does not exists we must block */
946
+ blockForKeys(c, c->argv + 1, c->argc - 2, timeout, NULL);
947
+ }
948
+
949
+ void blpopCommand(redisClient *c) {
950
+ blockingPopGenericCommand(c,REDIS_HEAD);
951
+ }
952
+
953
+ void brpopCommand(redisClient *c) {
954
+ blockingPopGenericCommand(c,REDIS_TAIL);
955
+ }
956
+
957
+ void brpoplpushCommand(redisClient *c) {
958
+ time_t timeout;
959
+
960
+ if (getTimeoutFromObjectOrReply(c,c->argv[3],&timeout) != REDIS_OK)
961
+ return;
962
+
963
+ robj *key = lookupKeyWrite(c->db, c->argv[1]);
964
+
965
+ if (key == NULL) {
966
+ if (c->flags & REDIS_MULTI) {
967
+
968
+ /* Blocking against an empty list in a multi state
969
+ * returns immediately. */
970
+ addReply(c, shared.nullbulk);
971
+ } else {
972
+ /* The list is empty and the client blocks. */
973
+ blockForKeys(c, c->argv + 1, 1, timeout, c->argv[2]);
974
+ }
975
+ } else {
976
+ if (key->type != REDIS_LIST) {
977
+ addReply(c, shared.wrongtypeerr);
978
+ } else {
979
+
980
+ /* The list exists and has elements, so
981
+ * the regular rpoplpushCommand is executed. */
982
+ redisAssert(listTypeLength(key) > 0);
983
+ rpoplpushCommand(c);
984
+ }
985
+ }
986
+ }