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,314 @@
1
+ #include "redis.h"
2
+ #include "sha1.h" /* SHA1 is used for DEBUG DIGEST */
3
+
4
+ #include <arpa/inet.h>
5
+
6
+ /* ================================= Debugging ============================== */
7
+
8
+ /* Compute the sha1 of string at 's' with 'len' bytes long.
9
+ * The SHA1 is then xored againt the string pointed by digest.
10
+ * Since xor is commutative, this operation is used in order to
11
+ * "add" digests relative to unordered elements.
12
+ *
13
+ * So digest(a,b,c,d) will be the same of digest(b,a,c,d) */
14
+ void xorDigest(unsigned char *digest, void *ptr, size_t len) {
15
+ SHA1_CTX ctx;
16
+ unsigned char hash[20], *s = ptr;
17
+ int j;
18
+
19
+ SHA1Init(&ctx);
20
+ SHA1Update(&ctx,s,len);
21
+ SHA1Final(hash,&ctx);
22
+
23
+ for (j = 0; j < 20; j++)
24
+ digest[j] ^= hash[j];
25
+ }
26
+
27
+ void xorObjectDigest(unsigned char *digest, robj *o) {
28
+ o = getDecodedObject(o);
29
+ xorDigest(digest,o->ptr,sdslen(o->ptr));
30
+ decrRefCount(o);
31
+ }
32
+
33
+ /* This function instead of just computing the SHA1 and xoring it
34
+ * against diget, also perform the digest of "digest" itself and
35
+ * replace the old value with the new one.
36
+ *
37
+ * So the final digest will be:
38
+ *
39
+ * digest = SHA1(digest xor SHA1(data))
40
+ *
41
+ * This function is used every time we want to preserve the order so
42
+ * that digest(a,b,c,d) will be different than digest(b,c,d,a)
43
+ *
44
+ * Also note that mixdigest("foo") followed by mixdigest("bar")
45
+ * will lead to a different digest compared to "fo", "obar".
46
+ */
47
+ void mixDigest(unsigned char *digest, void *ptr, size_t len) {
48
+ SHA1_CTX ctx;
49
+ char *s = ptr;
50
+
51
+ xorDigest(digest,s,len);
52
+ SHA1Init(&ctx);
53
+ SHA1Update(&ctx,digest,20);
54
+ SHA1Final(digest,&ctx);
55
+ }
56
+
57
+ void mixObjectDigest(unsigned char *digest, robj *o) {
58
+ o = getDecodedObject(o);
59
+ mixDigest(digest,o->ptr,sdslen(o->ptr));
60
+ decrRefCount(o);
61
+ }
62
+
63
+ /* Compute the dataset digest. Since keys, sets elements, hashes elements
64
+ * are not ordered, we use a trick: every aggregate digest is the xor
65
+ * of the digests of their elements. This way the order will not change
66
+ * the result. For list instead we use a feedback entering the output digest
67
+ * as input in order to ensure that a different ordered list will result in
68
+ * a different digest. */
69
+ void computeDatasetDigest(unsigned char *final) {
70
+ unsigned char digest[20];
71
+ char buf[128];
72
+ dictIterator *di = NULL;
73
+ dictEntry *de;
74
+ int j;
75
+ uint32_t aux;
76
+
77
+ memset(final,0,20); /* Start with a clean result */
78
+
79
+ for (j = 0; j < server.dbnum; j++) {
80
+ redisDb *db = server.db+j;
81
+
82
+ if (dictSize(db->dict) == 0) continue;
83
+ di = dictGetSafeIterator(db->dict);
84
+
85
+ /* hash the DB id, so the same dataset moved in a different
86
+ * DB will lead to a different digest */
87
+ aux = htonl(j);
88
+ mixDigest(final,&aux,sizeof(aux));
89
+
90
+ /* Iterate this DB writing every entry */
91
+ while((de = dictNext(di)) != NULL) {
92
+ sds key;
93
+ robj *keyobj, *o;
94
+ time_t expiretime;
95
+
96
+ memset(digest,0,20); /* This key-val digest */
97
+ key = dictGetEntryKey(de);
98
+ keyobj = createStringObject(key,sdslen(key));
99
+
100
+ mixDigest(digest,key,sdslen(key));
101
+
102
+ /* Make sure the key is loaded if VM is active */
103
+ o = lookupKeyRead(db,keyobj);
104
+ if (o == NULL) {
105
+ /* Key expired on lookup? Try the next one. */
106
+ decrRefCount(keyobj);
107
+ continue;
108
+ }
109
+
110
+ aux = htonl(o->type);
111
+ mixDigest(digest,&aux,sizeof(aux));
112
+ expiretime = getExpire(db,keyobj);
113
+
114
+ /* Save the key and associated value */
115
+ if (o->type == REDIS_STRING) {
116
+ mixObjectDigest(digest,o);
117
+ } else if (o->type == REDIS_LIST) {
118
+ listTypeIterator *li = listTypeInitIterator(o,0,REDIS_TAIL);
119
+ listTypeEntry entry;
120
+ while(listTypeNext(li,&entry)) {
121
+ robj *eleobj = listTypeGet(&entry);
122
+ mixObjectDigest(digest,eleobj);
123
+ decrRefCount(eleobj);
124
+ }
125
+ listTypeReleaseIterator(li);
126
+ } else if (o->type == REDIS_SET) {
127
+ setTypeIterator *si = setTypeInitIterator(o);
128
+ robj *ele;
129
+ while((ele = setTypeNextObject(si)) != NULL) {
130
+ xorObjectDigest(digest,ele);
131
+ decrRefCount(ele);
132
+ }
133
+ setTypeReleaseIterator(si);
134
+ } else if (o->type == REDIS_ZSET) {
135
+ zset *zs = o->ptr;
136
+ dictIterator *di = dictGetIterator(zs->dict);
137
+ dictEntry *de;
138
+
139
+ while((de = dictNext(di)) != NULL) {
140
+ robj *eleobj = dictGetEntryKey(de);
141
+ double *score = dictGetEntryVal(de);
142
+ unsigned char eledigest[20];
143
+
144
+ snprintf(buf,sizeof(buf),"%.17g",*score);
145
+ memset(eledigest,0,20);
146
+ mixObjectDigest(eledigest,eleobj);
147
+ mixDigest(eledigest,buf,strlen(buf));
148
+ xorDigest(digest,eledigest,20);
149
+ }
150
+ dictReleaseIterator(di);
151
+ } else if (o->type == REDIS_HASH) {
152
+ hashTypeIterator *hi;
153
+ robj *obj;
154
+
155
+ hi = hashTypeInitIterator(o);
156
+ while (hashTypeNext(hi) != REDIS_ERR) {
157
+ unsigned char eledigest[20];
158
+
159
+ memset(eledigest,0,20);
160
+ obj = hashTypeCurrentObject(hi,REDIS_HASH_KEY);
161
+ mixObjectDigest(eledigest,obj);
162
+ decrRefCount(obj);
163
+ obj = hashTypeCurrentObject(hi,REDIS_HASH_VALUE);
164
+ mixObjectDigest(eledigest,obj);
165
+ decrRefCount(obj);
166
+ xorDigest(digest,eledigest,20);
167
+ }
168
+ hashTypeReleaseIterator(hi);
169
+ } else {
170
+ redisPanic("Unknown object type");
171
+ }
172
+ /* If the key has an expire, add it to the mix */
173
+ if (expiretime != -1) xorDigest(digest,"!!expire!!",10);
174
+ /* We can finally xor the key-val digest to the final digest */
175
+ xorDigest(final,digest,20);
176
+ decrRefCount(keyobj);
177
+ }
178
+ dictReleaseIterator(di);
179
+ }
180
+ }
181
+
182
+ void debugCommand(redisClient *c) {
183
+ if (!strcasecmp(c->argv[1]->ptr,"segfault")) {
184
+ *((char*)-1) = 'x';
185
+ } else if (!strcasecmp(c->argv[1]->ptr,"reload")) {
186
+ if (rdbSave(server.dbfilename) != REDIS_OK) {
187
+ addReply(c,shared.err);
188
+ return;
189
+ }
190
+ emptyDb();
191
+ if (rdbLoad(server.dbfilename) != REDIS_OK) {
192
+ addReply(c,shared.err);
193
+ return;
194
+ }
195
+ redisLog(REDIS_WARNING,"DB reloaded by DEBUG RELOAD");
196
+ addReply(c,shared.ok);
197
+ } else if (!strcasecmp(c->argv[1]->ptr,"loadaof")) {
198
+ emptyDb();
199
+ if (loadAppendOnlyFile(server.appendfilename) != REDIS_OK) {
200
+ addReply(c,shared.err);
201
+ return;
202
+ }
203
+ redisLog(REDIS_WARNING,"Append Only File loaded by DEBUG LOADAOF");
204
+ addReply(c,shared.ok);
205
+ } else if (!strcasecmp(c->argv[1]->ptr,"object") && c->argc == 3) {
206
+ dictEntry *de = dictFind(c->db->dict,c->argv[2]->ptr);
207
+ robj *val;
208
+
209
+ if (!de) {
210
+ addReply(c,shared.nokeyerr);
211
+ return;
212
+ }
213
+ val = dictGetEntryVal(de);
214
+ if (!server.vm_enabled || (val->storage == REDIS_VM_MEMORY ||
215
+ val->storage == REDIS_VM_SWAPPING)) {
216
+ char *strenc;
217
+
218
+ strenc = strEncoding(val->encoding);
219
+ addReplyStatusFormat(c,
220
+ "Value at:%p refcount:%d "
221
+ "encoding:%s serializedlength:%lld "
222
+ "lru:%d lru_seconds_idle:%lu",
223
+ (void*)val, val->refcount,
224
+ strenc, (long long) rdbSavedObjectLen(val),
225
+ val->lru, estimateObjectIdleTime(val));
226
+ } else {
227
+ vmpointer *vp = (vmpointer*) val;
228
+ addReplyStatusFormat(c,
229
+ "Value swapped at: page %llu "
230
+ "using %llu pages",
231
+ (unsigned long long) vp->page,
232
+ (unsigned long long) vp->usedpages);
233
+ }
234
+ } else if (!strcasecmp(c->argv[1]->ptr,"swapin") && c->argc == 3) {
235
+ lookupKeyRead(c->db,c->argv[2]);
236
+ addReply(c,shared.ok);
237
+ } else if (!strcasecmp(c->argv[1]->ptr,"swapout") && c->argc == 3) {
238
+ dictEntry *de = dictFind(c->db->dict,c->argv[2]->ptr);
239
+ robj *val;
240
+ vmpointer *vp;
241
+
242
+ if (!server.vm_enabled) {
243
+ addReplyError(c,"Virtual Memory is disabled");
244
+ return;
245
+ }
246
+ if (!de) {
247
+ addReply(c,shared.nokeyerr);
248
+ return;
249
+ }
250
+ val = dictGetEntryVal(de);
251
+ /* Swap it */
252
+ if (val->storage != REDIS_VM_MEMORY) {
253
+ addReplyError(c,"This key is not in memory");
254
+ } else if (val->refcount != 1) {
255
+ addReplyError(c,"Object is shared");
256
+ } else if ((vp = vmSwapObjectBlocking(val)) != NULL) {
257
+ dictGetEntryVal(de) = vp;
258
+ addReply(c,shared.ok);
259
+ } else {
260
+ addReply(c,shared.err);
261
+ }
262
+ } else if (!strcasecmp(c->argv[1]->ptr,"populate") && c->argc == 3) {
263
+ long keys, j;
264
+ robj *key, *val;
265
+ char buf[128];
266
+
267
+ if (getLongFromObjectOrReply(c, c->argv[2], &keys, NULL) != REDIS_OK)
268
+ return;
269
+ for (j = 0; j < keys; j++) {
270
+ snprintf(buf,sizeof(buf),"key:%lu",j);
271
+ key = createStringObject(buf,strlen(buf));
272
+ if (lookupKeyRead(c->db,key) != NULL) {
273
+ decrRefCount(key);
274
+ continue;
275
+ }
276
+ snprintf(buf,sizeof(buf),"value:%lu",j);
277
+ val = createStringObject(buf,strlen(buf));
278
+ dbAdd(c->db,key,val);
279
+ decrRefCount(key);
280
+ }
281
+ addReply(c,shared.ok);
282
+ } else if (!strcasecmp(c->argv[1]->ptr,"digest") && c->argc == 2) {
283
+ unsigned char digest[20];
284
+ sds d = sdsempty();
285
+ int j;
286
+
287
+ computeDatasetDigest(digest);
288
+ for (j = 0; j < 20; j++)
289
+ d = sdscatprintf(d, "%02x",digest[j]);
290
+ addReplyStatus(c,d);
291
+ sdsfree(d);
292
+ } else {
293
+ addReplyError(c,
294
+ "Syntax error, try DEBUG [SEGFAULT|OBJECT <key>|SWAPIN <key>|SWAPOUT <key>|RELOAD]");
295
+ }
296
+ }
297
+
298
+ void _redisAssert(char *estr, char *file, int line) {
299
+ redisLog(REDIS_WARNING,"=== ASSERTION FAILED ===");
300
+ redisLog(REDIS_WARNING,"==> %s:%d '%s' is not true",file,line,estr);
301
+ #ifdef HAVE_BACKTRACE
302
+ redisLog(REDIS_WARNING,"(forcing SIGSEGV in order to print the stack trace)");
303
+ *((char*)-1) = 'x';
304
+ #endif
305
+ }
306
+
307
+ void _redisPanic(char *msg, char *file, int line) {
308
+ redisLog(REDIS_WARNING,"!!! Software Failure. Press left mouse button to continue");
309
+ redisLog(REDIS_WARNING,"Guru Meditation: %s #%s:%d",msg,file,line);
310
+ #ifdef HAVE_BACKTRACE
311
+ redisLog(REDIS_WARNING,"(forcing SIGSEGV in order to print the stack trace)");
312
+ *((char*)-1) = 'x';
313
+ #endif
314
+ }
@@ -0,0 +1,721 @@
1
+ /* Hash Tables Implementation.
2
+ *
3
+ * This file implements in memory hash tables with insert/del/replace/find/
4
+ * get-random-element operations. Hash tables will auto resize if needed
5
+ * tables of power of two in size are used, collisions are handled by
6
+ * chaining. See the source code for more information... :)
7
+ *
8
+ * Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
9
+ * All rights reserved.
10
+ *
11
+ * Redistribution and use in source and binary forms, with or without
12
+ * modification, are permitted provided that the following conditions are met:
13
+ *
14
+ * * Redistributions of source code must retain the above copyright notice,
15
+ * this list of conditions and the following disclaimer.
16
+ * * Redistributions in binary form must reproduce the above copyright
17
+ * notice, this list of conditions and the following disclaimer in the
18
+ * documentation and/or other materials provided with the distribution.
19
+ * * Neither the name of Redis nor the names of its contributors may be used
20
+ * to endorse or promote products derived from this software without
21
+ * specific prior written permission.
22
+ *
23
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
27
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33
+ * POSSIBILITY OF SUCH DAMAGE.
34
+ */
35
+
36
+ #include "fmacros.h"
37
+
38
+ #include <stdio.h>
39
+ #include <stdlib.h>
40
+ #include <string.h>
41
+ #include <stdarg.h>
42
+ #include <assert.h>
43
+ #include <limits.h>
44
+ #include <sys/time.h>
45
+ #include <ctype.h>
46
+
47
+ #include "dict.h"
48
+ #include "zmalloc.h"
49
+
50
+ /* Using dictEnableResize() / dictDisableResize() we make possible to
51
+ * enable/disable resizing of the hash table as needed. This is very important
52
+ * for Redis, as we use copy-on-write and don't want to move too much memory
53
+ * around when there is a child performing saving operations.
54
+ *
55
+ * Note that even when dict_can_resize is set to 0, not all resizes are
56
+ * prevented: an hash table is still allowed to grow if the ratio between
57
+ * the number of elements and the buckets > dict_force_resize_ratio. */
58
+ static int dict_can_resize = 1;
59
+ static unsigned int dict_force_resize_ratio = 5;
60
+
61
+ /* -------------------------- private prototypes ---------------------------- */
62
+
63
+ static int _dictExpandIfNeeded(dict *ht);
64
+ static unsigned long _dictNextPower(unsigned long size);
65
+ static int _dictKeyIndex(dict *ht, const void *key);
66
+ static int _dictInit(dict *ht, dictType *type, void *privDataPtr);
67
+
68
+ /* -------------------------- hash functions -------------------------------- */
69
+
70
+ /* Thomas Wang's 32 bit Mix Function */
71
+ unsigned int dictIntHashFunction(unsigned int key)
72
+ {
73
+ key += ~(key << 15);
74
+ key ^= (key >> 10);
75
+ key += (key << 3);
76
+ key ^= (key >> 6);
77
+ key += ~(key << 11);
78
+ key ^= (key >> 16);
79
+ return key;
80
+ }
81
+
82
+ /* Identity hash function for integer keys */
83
+ unsigned int dictIdentityHashFunction(unsigned int key)
84
+ {
85
+ return key;
86
+ }
87
+
88
+ /* Generic hash function (a popular one from Bernstein).
89
+ * I tested a few and this was the best. */
90
+ unsigned int dictGenHashFunction(const unsigned char *buf, int len) {
91
+ unsigned int hash = 5381;
92
+
93
+ while (len--)
94
+ hash = ((hash << 5) + hash) + (*buf++); /* hash * 33 + c */
95
+ return hash;
96
+ }
97
+
98
+ /* And a case insensitive version */
99
+ unsigned int dictGenCaseHashFunction(const unsigned char *buf, int len) {
100
+ unsigned int hash = 5381;
101
+
102
+ while (len--)
103
+ hash = ((hash << 5) + hash) + (tolower(*buf++)); /* hash * 33 + c */
104
+ return hash;
105
+ }
106
+
107
+ /* ----------------------------- API implementation ------------------------- */
108
+
109
+ /* Reset an hashtable already initialized with ht_init().
110
+ * NOTE: This function should only called by ht_destroy(). */
111
+ static void _dictReset(dictht *ht)
112
+ {
113
+ ht->table = NULL;
114
+ ht->size = 0;
115
+ ht->sizemask = 0;
116
+ ht->used = 0;
117
+ }
118
+
119
+ /* Create a new hash table */
120
+ dict *dictCreate(dictType *type,
121
+ void *privDataPtr)
122
+ {
123
+ dict *d = zmalloc(sizeof(*d));
124
+
125
+ _dictInit(d,type,privDataPtr);
126
+ return d;
127
+ }
128
+
129
+ /* Initialize the hash table */
130
+ int _dictInit(dict *d, dictType *type,
131
+ void *privDataPtr)
132
+ {
133
+ _dictReset(&d->ht[0]);
134
+ _dictReset(&d->ht[1]);
135
+ d->type = type;
136
+ d->privdata = privDataPtr;
137
+ d->rehashidx = -1;
138
+ d->iterators = 0;
139
+ return DICT_OK;
140
+ }
141
+
142
+ /* Resize the table to the minimal size that contains all the elements,
143
+ * but with the invariant of a USER/BUCKETS ratio near to <= 1 */
144
+ int dictResize(dict *d)
145
+ {
146
+ int minimal;
147
+
148
+ if (!dict_can_resize || dictIsRehashing(d)) return DICT_ERR;
149
+ minimal = d->ht[0].used;
150
+ if (minimal < DICT_HT_INITIAL_SIZE)
151
+ minimal = DICT_HT_INITIAL_SIZE;
152
+ return dictExpand(d, minimal);
153
+ }
154
+
155
+ /* Expand or create the hashtable */
156
+ int dictExpand(dict *d, unsigned long size)
157
+ {
158
+ dictht n; /* the new hashtable */
159
+ unsigned long realsize = _dictNextPower(size);
160
+
161
+ /* the size is invalid if it is smaller than the number of
162
+ * elements already inside the hashtable */
163
+ if (dictIsRehashing(d) || d->ht[0].used > size)
164
+ return DICT_ERR;
165
+
166
+ /* Allocate the new hashtable and initialize all pointers to NULL */
167
+ n.size = realsize;
168
+ n.sizemask = realsize-1;
169
+ n.table = zcalloc(realsize*sizeof(dictEntry*));
170
+ n.used = 0;
171
+
172
+ /* Is this the first initialization? If so it's not really a rehashing
173
+ * we just set the first hash table so that it can accept keys. */
174
+ if (d->ht[0].table == NULL) {
175
+ d->ht[0] = n;
176
+ return DICT_OK;
177
+ }
178
+
179
+ /* Prepare a second hash table for incremental rehashing */
180
+ d->ht[1] = n;
181
+ d->rehashidx = 0;
182
+ return DICT_OK;
183
+ }
184
+
185
+ /* Performs N steps of incremental rehashing. Returns 1 if there are still
186
+ * keys to move from the old to the new hash table, otherwise 0 is returned.
187
+ * Note that a rehashing step consists in moving a bucket (that may have more
188
+ * thank one key as we use chaining) from the old to the new hash table. */
189
+ int dictRehash(dict *d, int n) {
190
+ if (!dictIsRehashing(d)) return 0;
191
+
192
+ while(n--) {
193
+ dictEntry *de, *nextde;
194
+
195
+ /* Check if we already rehashed the whole table... */
196
+ if (d->ht[0].used == 0) {
197
+ zfree(d->ht[0].table);
198
+ d->ht[0] = d->ht[1];
199
+ _dictReset(&d->ht[1]);
200
+ d->rehashidx = -1;
201
+ return 0;
202
+ }
203
+
204
+ /* Note that rehashidx can't overflow as we are sure there are more
205
+ * elements because ht[0].used != 0 */
206
+ while(d->ht[0].table[d->rehashidx] == NULL) d->rehashidx++;
207
+ de = d->ht[0].table[d->rehashidx];
208
+ /* Move all the keys in this bucket from the old to the new hash HT */
209
+ while(de) {
210
+ unsigned int h;
211
+
212
+ nextde = de->next;
213
+ /* Get the index in the new hash table */
214
+ h = dictHashKey(d, de->key) & d->ht[1].sizemask;
215
+ de->next = d->ht[1].table[h];
216
+ d->ht[1].table[h] = de;
217
+ d->ht[0].used--;
218
+ d->ht[1].used++;
219
+ de = nextde;
220
+ }
221
+ d->ht[0].table[d->rehashidx] = NULL;
222
+ d->rehashidx++;
223
+ }
224
+ return 1;
225
+ }
226
+
227
+ long long timeInMilliseconds(void) {
228
+ struct timeval tv;
229
+
230
+ gettimeofday(&tv,NULL);
231
+ return (((long long)tv.tv_sec)*1000)+(tv.tv_usec/1000);
232
+ }
233
+
234
+ /* Rehash for an amount of time between ms milliseconds and ms+1 milliseconds */
235
+ int dictRehashMilliseconds(dict *d, int ms) {
236
+ long long start = timeInMilliseconds();
237
+ int rehashes = 0;
238
+
239
+ while(dictRehash(d,100)) {
240
+ rehashes += 100;
241
+ if (timeInMilliseconds()-start > ms) break;
242
+ }
243
+ return rehashes;
244
+ }
245
+
246
+ /* This function performs just a step of rehashing, and only if there are
247
+ * no safe iterators bound to our hash table. When we have iterators in the
248
+ * middle of a rehashing we can't mess with the two hash tables otherwise
249
+ * some element can be missed or duplicated.
250
+ *
251
+ * This function is called by common lookup or update operations in the
252
+ * dictionary so that the hash table automatically migrates from H1 to H2
253
+ * while it is actively used. */
254
+ static void _dictRehashStep(dict *d) {
255
+ if (d->iterators == 0) dictRehash(d,1);
256
+ }
257
+
258
+ /* Add an element to the target hash table */
259
+ int dictAdd(dict *d, void *key, void *val)
260
+ {
261
+ int index;
262
+ dictEntry *entry;
263
+ dictht *ht;
264
+
265
+ if (dictIsRehashing(d)) _dictRehashStep(d);
266
+
267
+ /* Get the index of the new element, or -1 if
268
+ * the element already exists. */
269
+ if ((index = _dictKeyIndex(d, key)) == -1)
270
+ return DICT_ERR;
271
+
272
+ /* Allocates the memory and stores key */
273
+ ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0];
274
+ entry = zmalloc(sizeof(*entry));
275
+ entry->next = ht->table[index];
276
+ ht->table[index] = entry;
277
+ ht->used++;
278
+
279
+ /* Set the hash entry fields. */
280
+ dictSetHashKey(d, entry, key);
281
+ dictSetHashVal(d, entry, val);
282
+ return DICT_OK;
283
+ }
284
+
285
+ /* Add an element, discarding the old if the key already exists.
286
+ * Return 1 if the key was added from scratch, 0 if there was already an
287
+ * element with such key and dictReplace() just performed a value update
288
+ * operation. */
289
+ int dictReplace(dict *d, void *key, void *val)
290
+ {
291
+ dictEntry *entry, auxentry;
292
+
293
+ /* Try to add the element. If the key
294
+ * does not exists dictAdd will suceed. */
295
+ if (dictAdd(d, key, val) == DICT_OK)
296
+ return 1;
297
+ /* It already exists, get the entry */
298
+ entry = dictFind(d, key);
299
+ /* Free the old value and set the new one */
300
+ /* Set the new value and free the old one. Note that it is important
301
+ * to do that in this order, as the value may just be exactly the same
302
+ * as the previous one. In this context, think to reference counting,
303
+ * you want to increment (set), and then decrement (free), and not the
304
+ * reverse. */
305
+ auxentry = *entry;
306
+ dictSetHashVal(d, entry, val);
307
+ dictFreeEntryVal(d, &auxentry);
308
+ return 0;
309
+ }
310
+
311
+ /* Search and remove an element */
312
+ static int dictGenericDelete(dict *d, const void *key, int nofree)
313
+ {
314
+ unsigned int h, idx;
315
+ dictEntry *he, *prevHe;
316
+ int table;
317
+
318
+ if (d->ht[0].size == 0) return DICT_ERR; /* d->ht[0].table is NULL */
319
+ if (dictIsRehashing(d)) _dictRehashStep(d);
320
+ h = dictHashKey(d, key);
321
+
322
+ for (table = 0; table <= 1; table++) {
323
+ idx = h & d->ht[table].sizemask;
324
+ he = d->ht[table].table[idx];
325
+ prevHe = NULL;
326
+ while(he) {
327
+ if (dictCompareHashKeys(d, key, he->key)) {
328
+ /* Unlink the element from the list */
329
+ if (prevHe)
330
+ prevHe->next = he->next;
331
+ else
332
+ d->ht[table].table[idx] = he->next;
333
+ if (!nofree) {
334
+ dictFreeEntryKey(d, he);
335
+ dictFreeEntryVal(d, he);
336
+ }
337
+ zfree(he);
338
+ d->ht[table].used--;
339
+ return DICT_OK;
340
+ }
341
+ prevHe = he;
342
+ he = he->next;
343
+ }
344
+ if (!dictIsRehashing(d)) break;
345
+ }
346
+ return DICT_ERR; /* not found */
347
+ }
348
+
349
+ int dictDelete(dict *ht, const void *key) {
350
+ return dictGenericDelete(ht,key,0);
351
+ }
352
+
353
+ int dictDeleteNoFree(dict *ht, const void *key) {
354
+ return dictGenericDelete(ht,key,1);
355
+ }
356
+
357
+ /* Destroy an entire dictionary */
358
+ int _dictClear(dict *d, dictht *ht)
359
+ {
360
+ unsigned long i;
361
+
362
+ /* Free all the elements */
363
+ for (i = 0; i < ht->size && ht->used > 0; i++) {
364
+ dictEntry *he, *nextHe;
365
+
366
+ if ((he = ht->table[i]) == NULL) continue;
367
+ while(he) {
368
+ nextHe = he->next;
369
+ dictFreeEntryKey(d, he);
370
+ dictFreeEntryVal(d, he);
371
+ zfree(he);
372
+ ht->used--;
373
+ he = nextHe;
374
+ }
375
+ }
376
+ /* Free the table and the allocated cache structure */
377
+ zfree(ht->table);
378
+ /* Re-initialize the table */
379
+ _dictReset(ht);
380
+ return DICT_OK; /* never fails */
381
+ }
382
+
383
+ /* Clear & Release the hash table */
384
+ void dictRelease(dict *d)
385
+ {
386
+ _dictClear(d,&d->ht[0]);
387
+ _dictClear(d,&d->ht[1]);
388
+ zfree(d);
389
+ }
390
+
391
+ dictEntry *dictFind(dict *d, const void *key)
392
+ {
393
+ dictEntry *he;
394
+ unsigned int h, idx, table;
395
+
396
+ if (d->ht[0].size == 0) return NULL; /* We don't have a table at all */
397
+ if (dictIsRehashing(d)) _dictRehashStep(d);
398
+ h = dictHashKey(d, key);
399
+ for (table = 0; table <= 1; table++) {
400
+ idx = h & d->ht[table].sizemask;
401
+ he = d->ht[table].table[idx];
402
+ while(he) {
403
+ if (dictCompareHashKeys(d, key, he->key))
404
+ return he;
405
+ he = he->next;
406
+ }
407
+ if (!dictIsRehashing(d)) return NULL;
408
+ }
409
+ return NULL;
410
+ }
411
+
412
+ void *dictFetchValue(dict *d, const void *key) {
413
+ dictEntry *he;
414
+
415
+ he = dictFind(d,key);
416
+ return he ? dictGetEntryVal(he) : NULL;
417
+ }
418
+
419
+ dictIterator *dictGetIterator(dict *d)
420
+ {
421
+ dictIterator *iter = zmalloc(sizeof(*iter));
422
+
423
+ iter->d = d;
424
+ iter->table = 0;
425
+ iter->index = -1;
426
+ iter->safe = 0;
427
+ iter->entry = NULL;
428
+ iter->nextEntry = NULL;
429
+ return iter;
430
+ }
431
+
432
+ dictIterator *dictGetSafeIterator(dict *d) {
433
+ dictIterator *i = dictGetIterator(d);
434
+
435
+ i->safe = 1;
436
+ return i;
437
+ }
438
+
439
+ dictEntry *dictNext(dictIterator *iter)
440
+ {
441
+ while (1) {
442
+ if (iter->entry == NULL) {
443
+ dictht *ht = &iter->d->ht[iter->table];
444
+ if (iter->safe && iter->index == -1 && iter->table == 0)
445
+ iter->d->iterators++;
446
+ iter->index++;
447
+ if (iter->index >= (signed) ht->size) {
448
+ if (dictIsRehashing(iter->d) && iter->table == 0) {
449
+ iter->table++;
450
+ iter->index = 0;
451
+ ht = &iter->d->ht[1];
452
+ } else {
453
+ break;
454
+ }
455
+ }
456
+ iter->entry = ht->table[iter->index];
457
+ } else {
458
+ iter->entry = iter->nextEntry;
459
+ }
460
+ if (iter->entry) {
461
+ /* We need to save the 'next' here, the iterator user
462
+ * may delete the entry we are returning. */
463
+ iter->nextEntry = iter->entry->next;
464
+ return iter->entry;
465
+ }
466
+ }
467
+ return NULL;
468
+ }
469
+
470
+ void dictReleaseIterator(dictIterator *iter)
471
+ {
472
+ if (iter->safe && !(iter->index == -1 && iter->table == 0))
473
+ iter->d->iterators--;
474
+ zfree(iter);
475
+ }
476
+
477
+ /* Return a random entry from the hash table. Useful to
478
+ * implement randomized algorithms */
479
+ dictEntry *dictGetRandomKey(dict *d)
480
+ {
481
+ dictEntry *he, *orighe;
482
+ unsigned int h;
483
+ int listlen, listele;
484
+
485
+ if (dictSize(d) == 0) return NULL;
486
+ if (dictIsRehashing(d)) _dictRehashStep(d);
487
+ if (dictIsRehashing(d)) {
488
+ do {
489
+ h = random() % (d->ht[0].size+d->ht[1].size);
490
+ he = (h >= d->ht[0].size) ? d->ht[1].table[h - d->ht[0].size] :
491
+ d->ht[0].table[h];
492
+ } while(he == NULL);
493
+ } else {
494
+ do {
495
+ h = random() & d->ht[0].sizemask;
496
+ he = d->ht[0].table[h];
497
+ } while(he == NULL);
498
+ }
499
+
500
+ /* Now we found a non empty bucket, but it is a linked
501
+ * list and we need to get a random element from the list.
502
+ * The only sane way to do so is counting the elements and
503
+ * select a random index. */
504
+ listlen = 0;
505
+ orighe = he;
506
+ while(he) {
507
+ he = he->next;
508
+ listlen++;
509
+ }
510
+ listele = random() % listlen;
511
+ he = orighe;
512
+ while(listele--) he = he->next;
513
+ return he;
514
+ }
515
+
516
+ /* ------------------------- private functions ------------------------------ */
517
+
518
+ /* Expand the hash table if needed */
519
+ static int _dictExpandIfNeeded(dict *d)
520
+ {
521
+ /* Incremental rehashing already in progress. Return. */
522
+ if (dictIsRehashing(d)) return DICT_OK;
523
+
524
+ /* If the hash table is empty expand it to the intial size. */
525
+ if (d->ht[0].size == 0) return dictExpand(d, DICT_HT_INITIAL_SIZE);
526
+
527
+ /* If we reached the 1:1 ratio, and we are allowed to resize the hash
528
+ * table (global setting) or we should avoid it but the ratio between
529
+ * elements/buckets is over the "safe" threshold, we resize doubling
530
+ * the number of buckets. */
531
+ if (d->ht[0].used >= d->ht[0].size &&
532
+ (dict_can_resize ||
533
+ d->ht[0].used/d->ht[0].size > dict_force_resize_ratio))
534
+ {
535
+ return dictExpand(d, ((d->ht[0].size > d->ht[0].used) ?
536
+ d->ht[0].size : d->ht[0].used)*2);
537
+ }
538
+ return DICT_OK;
539
+ }
540
+
541
+ /* Our hash table capability is a power of two */
542
+ static unsigned long _dictNextPower(unsigned long size)
543
+ {
544
+ unsigned long i = DICT_HT_INITIAL_SIZE;
545
+
546
+ if (size >= LONG_MAX) return LONG_MAX;
547
+ while(1) {
548
+ if (i >= size)
549
+ return i;
550
+ i *= 2;
551
+ }
552
+ }
553
+
554
+ /* Returns the index of a free slot that can be populated with
555
+ * an hash entry for the given 'key'.
556
+ * If the key already exists, -1 is returned.
557
+ *
558
+ * Note that if we are in the process of rehashing the hash table, the
559
+ * index is always returned in the context of the second (new) hash table. */
560
+ static int _dictKeyIndex(dict *d, const void *key)
561
+ {
562
+ unsigned int h, idx, table;
563
+ dictEntry *he;
564
+
565
+ /* Expand the hashtable if needed */
566
+ if (_dictExpandIfNeeded(d) == DICT_ERR)
567
+ return -1;
568
+ /* Compute the key hash value */
569
+ h = dictHashKey(d, key);
570
+ for (table = 0; table <= 1; table++) {
571
+ idx = h & d->ht[table].sizemask;
572
+ /* Search if this slot does not already contain the given key */
573
+ he = d->ht[table].table[idx];
574
+ while(he) {
575
+ if (dictCompareHashKeys(d, key, he->key))
576
+ return -1;
577
+ he = he->next;
578
+ }
579
+ if (!dictIsRehashing(d)) break;
580
+ }
581
+ return idx;
582
+ }
583
+
584
+ void dictEmpty(dict *d) {
585
+ _dictClear(d,&d->ht[0]);
586
+ _dictClear(d,&d->ht[1]);
587
+ d->rehashidx = -1;
588
+ d->iterators = 0;
589
+ }
590
+
591
+ #define DICT_STATS_VECTLEN 50
592
+ static void _dictPrintStatsHt(dictht *ht) {
593
+ unsigned long i, slots = 0, chainlen, maxchainlen = 0;
594
+ unsigned long totchainlen = 0;
595
+ unsigned long clvector[DICT_STATS_VECTLEN];
596
+
597
+ if (ht->used == 0) {
598
+ printf("No stats available for empty dictionaries\n");
599
+ return;
600
+ }
601
+
602
+ for (i = 0; i < DICT_STATS_VECTLEN; i++) clvector[i] = 0;
603
+ for (i = 0; i < ht->size; i++) {
604
+ dictEntry *he;
605
+
606
+ if (ht->table[i] == NULL) {
607
+ clvector[0]++;
608
+ continue;
609
+ }
610
+ slots++;
611
+ /* For each hash entry on this slot... */
612
+ chainlen = 0;
613
+ he = ht->table[i];
614
+ while(he) {
615
+ chainlen++;
616
+ he = he->next;
617
+ }
618
+ clvector[(chainlen < DICT_STATS_VECTLEN) ? chainlen : (DICT_STATS_VECTLEN-1)]++;
619
+ if (chainlen > maxchainlen) maxchainlen = chainlen;
620
+ totchainlen += chainlen;
621
+ }
622
+ printf("Hash table stats:\n");
623
+ printf(" table size: %ld\n", ht->size);
624
+ printf(" number of elements: %ld\n", ht->used);
625
+ printf(" different slots: %ld\n", slots);
626
+ printf(" max chain length: %ld\n", maxchainlen);
627
+ printf(" avg chain length (counted): %.02f\n", (float)totchainlen/slots);
628
+ printf(" avg chain length (computed): %.02f\n", (float)ht->used/slots);
629
+ printf(" Chain length distribution:\n");
630
+ for (i = 0; i < DICT_STATS_VECTLEN-1; i++) {
631
+ if (clvector[i] == 0) continue;
632
+ printf(" %s%ld: %ld (%.02f%%)\n",(i == DICT_STATS_VECTLEN-1)?">= ":"", i, clvector[i], ((float)clvector[i]/ht->size)*100);
633
+ }
634
+ }
635
+
636
+ void dictPrintStats(dict *d) {
637
+ _dictPrintStatsHt(&d->ht[0]);
638
+ if (dictIsRehashing(d)) {
639
+ printf("-- Rehashing into ht[1]:\n");
640
+ _dictPrintStatsHt(&d->ht[1]);
641
+ }
642
+ }
643
+
644
+ void dictEnableResize(void) {
645
+ dict_can_resize = 1;
646
+ }
647
+
648
+ void dictDisableResize(void) {
649
+ dict_can_resize = 0;
650
+ }
651
+
652
+ #if 0
653
+
654
+ /* The following are just example hash table types implementations.
655
+ * Not useful for Redis so they are commented out.
656
+ */
657
+
658
+ /* ----------------------- StringCopy Hash Table Type ------------------------*/
659
+
660
+ static unsigned int _dictStringCopyHTHashFunction(const void *key)
661
+ {
662
+ return dictGenHashFunction(key, strlen(key));
663
+ }
664
+
665
+ static void *_dictStringDup(void *privdata, const void *key)
666
+ {
667
+ int len = strlen(key);
668
+ char *copy = zmalloc(len+1);
669
+ DICT_NOTUSED(privdata);
670
+
671
+ memcpy(copy, key, len);
672
+ copy[len] = '\0';
673
+ return copy;
674
+ }
675
+
676
+ static int _dictStringCopyHTKeyCompare(void *privdata, const void *key1,
677
+ const void *key2)
678
+ {
679
+ DICT_NOTUSED(privdata);
680
+
681
+ return strcmp(key1, key2) == 0;
682
+ }
683
+
684
+ static void _dictStringDestructor(void *privdata, void *key)
685
+ {
686
+ DICT_NOTUSED(privdata);
687
+
688
+ zfree(key);
689
+ }
690
+
691
+ dictType dictTypeHeapStringCopyKey = {
692
+ _dictStringCopyHTHashFunction, /* hash function */
693
+ _dictStringDup, /* key dup */
694
+ NULL, /* val dup */
695
+ _dictStringCopyHTKeyCompare, /* key compare */
696
+ _dictStringDestructor, /* key destructor */
697
+ NULL /* val destructor */
698
+ };
699
+
700
+ /* This is like StringCopy but does not auto-duplicate the key.
701
+ * It's used for intepreter's shared strings. */
702
+ dictType dictTypeHeapStrings = {
703
+ _dictStringCopyHTHashFunction, /* hash function */
704
+ NULL, /* key dup */
705
+ NULL, /* val dup */
706
+ _dictStringCopyHTKeyCompare, /* key compare */
707
+ _dictStringDestructor, /* key destructor */
708
+ NULL /* val destructor */
709
+ };
710
+
711
+ /* This is like StringCopy but also automatically handle dynamic
712
+ * allocated C strings as values. */
713
+ dictType dictTypeHeapStringCopyKeyValue = {
714
+ _dictStringCopyHTHashFunction, /* hash function */
715
+ _dictStringDup, /* key dup */
716
+ _dictStringDup, /* val dup */
717
+ _dictStringCopyHTKeyCompare, /* key compare */
718
+ _dictStringDestructor, /* key destructor */
719
+ _dictStringDestructor, /* val destructor */
720
+ };
721
+ #endif