├── .github └── workflows │ └── ci.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── build.zig ├── justfile ├── rope-bench ├── Cargo.toml └── src │ └── main.rs └── src ├── cmd.zig ├── interop.zig ├── main.zig ├── reserve.zig ├── rope.zig ├── test.zig └── vendor ├── redismodule.h └── redismodule.zig /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | build_test: 13 | name: Build and Test 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v2 17 | 18 | - uses: goto-bus-stop/setup-zig@v1 19 | with: 20 | version: 0.9.0 21 | 22 | - uses: dtolnay/rust-toolchain@stable 23 | 24 | - uses: Swatinem/rust-cache@v1 25 | 26 | - uses: extractions/setup-just@v1 27 | 28 | - run: just test 29 | 30 | # From https://redis.io/docs/getting-started/installation/install-redis-on-linux/ 31 | - run: | 32 | curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg 33 | echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.list 34 | sudo apt-get update 35 | sudo apt-get install -y redis 36 | 37 | - run: just bench 38 | 39 | format_lint: 40 | name: Format and Lint 41 | runs-on: ubuntu-latest 42 | steps: 43 | - uses: actions/checkout@v2 44 | 45 | - uses: goto-bus-stop/setup-zig@v1 46 | with: 47 | version: 0.9.0 48 | 49 | - run: zig fmt --check . 50 | 51 | - uses: dtolnay/rust-toolchain@nightly 52 | with: 53 | components: clippy, rustfmt 54 | 55 | - uses: Swatinem/rust-cache@v1 56 | 57 | - run: cargo +nightly fmt -- --check 58 | 59 | - run: cargo clippy -- -D warnings 60 | 61 | artifacts: 62 | name: Artifacts 63 | runs-on: ubuntu-latest 64 | steps: 65 | - uses: actions/checkout@v2 66 | 67 | - uses: goto-bus-stop/setup-zig@v1 68 | with: 69 | version: 0.9.0 70 | 71 | - uses: extractions/setup-just@v1 72 | 73 | - run: just build-artifacts 74 | 75 | - uses: actions/upload-artifact@v3 76 | with: 77 | name: libredisrope 78 | path: zig-out/artifacts 79 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /zig-cache 2 | /zig-out 3 | /target 4 | dump.rdb 5 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ansi_term" 7 | version = "0.12.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" 10 | dependencies = [ 11 | "winapi", 12 | ] 13 | 14 | [[package]] 15 | name = "anyhow" 16 | version = "1.0.58" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704" 19 | 20 | [[package]] 21 | name = "async-trait" 22 | version = "0.1.56" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716" 25 | dependencies = [ 26 | "proc-macro2", 27 | "quote", 28 | "syn", 29 | ] 30 | 31 | [[package]] 32 | name = "atty" 33 | version = "0.2.14" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 36 | dependencies = [ 37 | "hermit-abi", 38 | "libc", 39 | "winapi", 40 | ] 41 | 42 | [[package]] 43 | name = "autocfg" 44 | version = "1.1.0" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 47 | 48 | [[package]] 49 | name = "bitflags" 50 | version = "1.3.2" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 53 | 54 | [[package]] 55 | name = "bytes" 56 | version = "1.2.0" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | checksum = "f0b3de4a0c5e67e16066a0715723abd91edc2f9001d09c46e1dca929351e130e" 59 | 60 | [[package]] 61 | name = "cfg-if" 62 | version = "1.0.0" 63 | source = "registry+https://github.com/rust-lang/crates.io-index" 64 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 65 | 66 | [[package]] 67 | name = "clap" 68 | version = "3.2.14" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | checksum = "54635806b078b7925d6e36810b1755f2a4b5b4d57560432c1ecf60bcbe10602b" 71 | dependencies = [ 72 | "atty", 73 | "bitflags", 74 | "clap_derive", 75 | "clap_lex", 76 | "indexmap", 77 | "once_cell", 78 | "strsim", 79 | "termcolor", 80 | "textwrap", 81 | ] 82 | 83 | [[package]] 84 | name = "clap_derive" 85 | version = "3.2.7" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "759bf187376e1afa7b85b959e6a664a3e7a95203415dba952ad19139e798f902" 88 | dependencies = [ 89 | "heck", 90 | "proc-macro-error", 91 | "proc-macro2", 92 | "quote", 93 | "syn", 94 | ] 95 | 96 | [[package]] 97 | name = "clap_lex" 98 | version = "0.2.4" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" 101 | dependencies = [ 102 | "os_str_bytes", 103 | ] 104 | 105 | [[package]] 106 | name = "combine" 107 | version = "4.6.4" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "2a604e93b79d1808327a6fca85a6f2d69de66461e7620f5a4cbf5fb4d1d7c948" 110 | dependencies = [ 111 | "bytes", 112 | "futures-core", 113 | "memchr", 114 | "pin-project-lite", 115 | "tokio", 116 | "tokio-util 0.7.3", 117 | ] 118 | 119 | [[package]] 120 | name = "dtoa" 121 | version = "0.4.8" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" 124 | 125 | [[package]] 126 | name = "form_urlencoded" 127 | version = "1.0.1" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" 130 | dependencies = [ 131 | "matches", 132 | "percent-encoding", 133 | ] 134 | 135 | [[package]] 136 | name = "futures-core" 137 | version = "0.3.21" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" 140 | 141 | [[package]] 142 | name = "futures-sink" 143 | version = "0.3.21" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" 146 | 147 | [[package]] 148 | name = "futures-task" 149 | version = "0.3.21" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" 152 | 153 | [[package]] 154 | name = "futures-util" 155 | version = "0.3.21" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" 158 | dependencies = [ 159 | "futures-core", 160 | "futures-sink", 161 | "futures-task", 162 | "pin-project-lite", 163 | "pin-utils", 164 | ] 165 | 166 | [[package]] 167 | name = "getrandom" 168 | version = "0.2.7" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" 171 | dependencies = [ 172 | "cfg-if", 173 | "libc", 174 | "wasi", 175 | ] 176 | 177 | [[package]] 178 | name = "hashbrown" 179 | version = "0.12.3" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 182 | 183 | [[package]] 184 | name = "heck" 185 | version = "0.4.0" 186 | source = "registry+https://github.com/rust-lang/crates.io-index" 187 | checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" 188 | 189 | [[package]] 190 | name = "hermit-abi" 191 | version = "0.1.19" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 194 | dependencies = [ 195 | "libc", 196 | ] 197 | 198 | [[package]] 199 | name = "idna" 200 | version = "0.2.3" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" 203 | dependencies = [ 204 | "matches", 205 | "unicode-bidi", 206 | "unicode-normalization", 207 | ] 208 | 209 | [[package]] 210 | name = "indexmap" 211 | version = "1.9.1" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" 214 | dependencies = [ 215 | "autocfg", 216 | "hashbrown", 217 | ] 218 | 219 | [[package]] 220 | name = "indoc" 221 | version = "1.0.6" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | checksum = "05a0bd019339e5d968b37855180087b7b9d512c5046fbd244cf8c95687927d6e" 224 | 225 | [[package]] 226 | name = "itoa" 227 | version = "0.4.8" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" 230 | 231 | [[package]] 232 | name = "libc" 233 | version = "0.2.126" 234 | source = "registry+https://github.com/rust-lang/crates.io-index" 235 | checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" 236 | 237 | [[package]] 238 | name = "lock_api" 239 | version = "0.4.7" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" 242 | dependencies = [ 243 | "autocfg", 244 | "scopeguard", 245 | ] 246 | 247 | [[package]] 248 | name = "log" 249 | version = "0.4.17" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 252 | dependencies = [ 253 | "cfg-if", 254 | ] 255 | 256 | [[package]] 257 | name = "matches" 258 | version = "0.1.9" 259 | source = "registry+https://github.com/rust-lang/crates.io-index" 260 | checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" 261 | 262 | [[package]] 263 | name = "memchr" 264 | version = "2.5.0" 265 | source = "registry+https://github.com/rust-lang/crates.io-index" 266 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 267 | 268 | [[package]] 269 | name = "memoffset" 270 | version = "0.6.5" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" 273 | dependencies = [ 274 | "autocfg", 275 | ] 276 | 277 | [[package]] 278 | name = "mio" 279 | version = "0.8.4" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" 282 | dependencies = [ 283 | "libc", 284 | "log", 285 | "wasi", 286 | "windows-sys", 287 | ] 288 | 289 | [[package]] 290 | name = "nix" 291 | version = "0.24.2" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc" 294 | dependencies = [ 295 | "bitflags", 296 | "cfg-if", 297 | "libc", 298 | "memoffset", 299 | ] 300 | 301 | [[package]] 302 | name = "num_cpus" 303 | version = "1.13.1" 304 | source = "registry+https://github.com/rust-lang/crates.io-index" 305 | checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" 306 | dependencies = [ 307 | "hermit-abi", 308 | "libc", 309 | ] 310 | 311 | [[package]] 312 | name = "once_cell" 313 | version = "1.13.0" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" 316 | 317 | [[package]] 318 | name = "os_str_bytes" 319 | version = "6.2.0" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "648001efe5d5c0102d8cea768e348da85d90af8ba91f0bea908f157951493cd4" 322 | 323 | [[package]] 324 | name = "parking_lot" 325 | version = "0.12.1" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 328 | dependencies = [ 329 | "lock_api", 330 | "parking_lot_core", 331 | ] 332 | 333 | [[package]] 334 | name = "parking_lot_core" 335 | version = "0.9.3" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" 338 | dependencies = [ 339 | "cfg-if", 340 | "libc", 341 | "redox_syscall", 342 | "smallvec", 343 | "windows-sys", 344 | ] 345 | 346 | [[package]] 347 | name = "percent-encoding" 348 | version = "2.1.0" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" 351 | 352 | [[package]] 353 | name = "pin-project-lite" 354 | version = "0.2.9" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" 357 | 358 | [[package]] 359 | name = "pin-utils" 360 | version = "0.1.0" 361 | source = "registry+https://github.com/rust-lang/crates.io-index" 362 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 363 | 364 | [[package]] 365 | name = "ppv-lite86" 366 | version = "0.2.16" 367 | source = "registry+https://github.com/rust-lang/crates.io-index" 368 | checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" 369 | 370 | [[package]] 371 | name = "proc-macro-error" 372 | version = "1.0.4" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 375 | dependencies = [ 376 | "proc-macro-error-attr", 377 | "proc-macro2", 378 | "quote", 379 | "syn", 380 | "version_check", 381 | ] 382 | 383 | [[package]] 384 | name = "proc-macro-error-attr" 385 | version = "1.0.4" 386 | source = "registry+https://github.com/rust-lang/crates.io-index" 387 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 388 | dependencies = [ 389 | "proc-macro2", 390 | "quote", 391 | "version_check", 392 | ] 393 | 394 | [[package]] 395 | name = "proc-macro2" 396 | version = "1.0.40" 397 | source = "registry+https://github.com/rust-lang/crates.io-index" 398 | checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" 399 | dependencies = [ 400 | "unicode-ident", 401 | ] 402 | 403 | [[package]] 404 | name = "quote" 405 | version = "1.0.20" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" 408 | dependencies = [ 409 | "proc-macro2", 410 | ] 411 | 412 | [[package]] 413 | name = "rand" 414 | version = "0.8.5" 415 | source = "registry+https://github.com/rust-lang/crates.io-index" 416 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 417 | dependencies = [ 418 | "libc", 419 | "rand_chacha", 420 | "rand_core", 421 | ] 422 | 423 | [[package]] 424 | name = "rand_chacha" 425 | version = "0.3.1" 426 | source = "registry+https://github.com/rust-lang/crates.io-index" 427 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 428 | dependencies = [ 429 | "ppv-lite86", 430 | "rand_core", 431 | ] 432 | 433 | [[package]] 434 | name = "rand_core" 435 | version = "0.6.3" 436 | source = "registry+https://github.com/rust-lang/crates.io-index" 437 | checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" 438 | dependencies = [ 439 | "getrandom", 440 | ] 441 | 442 | [[package]] 443 | name = "redis" 444 | version = "0.21.5" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | checksum = "1a80b5f38d7f5a020856a0e16e40a9cfabf88ae8f0e4c2dcd8a3114c1e470852" 447 | dependencies = [ 448 | "async-trait", 449 | "bytes", 450 | "combine", 451 | "dtoa", 452 | "futures-util", 453 | "itoa", 454 | "percent-encoding", 455 | "pin-project-lite", 456 | "sha1", 457 | "tokio", 458 | "tokio-util 0.6.10", 459 | "url", 460 | ] 461 | 462 | [[package]] 463 | name = "redox_syscall" 464 | version = "0.2.15" 465 | source = "registry+https://github.com/rust-lang/crates.io-index" 466 | checksum = "534cfe58d6a18cc17120fbf4635d53d14691c1fe4d951064df9bd326178d7d5a" 467 | dependencies = [ 468 | "bitflags", 469 | ] 470 | 471 | [[package]] 472 | name = "rope-bench" 473 | version = "0.1.0" 474 | dependencies = [ 475 | "ansi_term", 476 | "anyhow", 477 | "clap", 478 | "indoc", 479 | "nix", 480 | "rand", 481 | "redis", 482 | "tokio", 483 | ] 484 | 485 | [[package]] 486 | name = "scopeguard" 487 | version = "1.1.0" 488 | source = "registry+https://github.com/rust-lang/crates.io-index" 489 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 490 | 491 | [[package]] 492 | name = "sha1" 493 | version = "0.6.1" 494 | source = "registry+https://github.com/rust-lang/crates.io-index" 495 | checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" 496 | dependencies = [ 497 | "sha1_smol", 498 | ] 499 | 500 | [[package]] 501 | name = "sha1_smol" 502 | version = "1.0.0" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" 505 | 506 | [[package]] 507 | name = "signal-hook-registry" 508 | version = "1.4.0" 509 | source = "registry+https://github.com/rust-lang/crates.io-index" 510 | checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" 511 | dependencies = [ 512 | "libc", 513 | ] 514 | 515 | [[package]] 516 | name = "smallvec" 517 | version = "1.9.0" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" 520 | 521 | [[package]] 522 | name = "socket2" 523 | version = "0.4.4" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" 526 | dependencies = [ 527 | "libc", 528 | "winapi", 529 | ] 530 | 531 | [[package]] 532 | name = "strsim" 533 | version = "0.10.0" 534 | source = "registry+https://github.com/rust-lang/crates.io-index" 535 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 536 | 537 | [[package]] 538 | name = "syn" 539 | version = "1.0.98" 540 | source = "registry+https://github.com/rust-lang/crates.io-index" 541 | checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" 542 | dependencies = [ 543 | "proc-macro2", 544 | "quote", 545 | "unicode-ident", 546 | ] 547 | 548 | [[package]] 549 | name = "termcolor" 550 | version = "1.1.3" 551 | source = "registry+https://github.com/rust-lang/crates.io-index" 552 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" 553 | dependencies = [ 554 | "winapi-util", 555 | ] 556 | 557 | [[package]] 558 | name = "textwrap" 559 | version = "0.15.0" 560 | source = "registry+https://github.com/rust-lang/crates.io-index" 561 | checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" 562 | 563 | [[package]] 564 | name = "tinyvec" 565 | version = "1.6.0" 566 | source = "registry+https://github.com/rust-lang/crates.io-index" 567 | checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" 568 | dependencies = [ 569 | "tinyvec_macros", 570 | ] 571 | 572 | [[package]] 573 | name = "tinyvec_macros" 574 | version = "0.1.0" 575 | source = "registry+https://github.com/rust-lang/crates.io-index" 576 | checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" 577 | 578 | [[package]] 579 | name = "tokio" 580 | version = "1.20.0" 581 | source = "registry+https://github.com/rust-lang/crates.io-index" 582 | checksum = "57aec3cfa4c296db7255446efb4928a6be304b431a806216105542a67b6ca82e" 583 | dependencies = [ 584 | "autocfg", 585 | "bytes", 586 | "libc", 587 | "memchr", 588 | "mio", 589 | "num_cpus", 590 | "once_cell", 591 | "parking_lot", 592 | "pin-project-lite", 593 | "signal-hook-registry", 594 | "socket2", 595 | "tokio-macros", 596 | "winapi", 597 | ] 598 | 599 | [[package]] 600 | name = "tokio-macros" 601 | version = "1.8.0" 602 | source = "registry+https://github.com/rust-lang/crates.io-index" 603 | checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" 604 | dependencies = [ 605 | "proc-macro2", 606 | "quote", 607 | "syn", 608 | ] 609 | 610 | [[package]] 611 | name = "tokio-util" 612 | version = "0.6.10" 613 | source = "registry+https://github.com/rust-lang/crates.io-index" 614 | checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" 615 | dependencies = [ 616 | "bytes", 617 | "futures-core", 618 | "futures-sink", 619 | "log", 620 | "pin-project-lite", 621 | "tokio", 622 | ] 623 | 624 | [[package]] 625 | name = "tokio-util" 626 | version = "0.7.3" 627 | source = "registry+https://github.com/rust-lang/crates.io-index" 628 | checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45" 629 | dependencies = [ 630 | "bytes", 631 | "futures-core", 632 | "futures-sink", 633 | "pin-project-lite", 634 | "tokio", 635 | "tracing", 636 | ] 637 | 638 | [[package]] 639 | name = "tracing" 640 | version = "0.1.35" 641 | source = "registry+https://github.com/rust-lang/crates.io-index" 642 | checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" 643 | dependencies = [ 644 | "cfg-if", 645 | "pin-project-lite", 646 | "tracing-core", 647 | ] 648 | 649 | [[package]] 650 | name = "tracing-core" 651 | version = "0.1.28" 652 | source = "registry+https://github.com/rust-lang/crates.io-index" 653 | checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7" 654 | dependencies = [ 655 | "once_cell", 656 | ] 657 | 658 | [[package]] 659 | name = "unicode-bidi" 660 | version = "0.3.8" 661 | source = "registry+https://github.com/rust-lang/crates.io-index" 662 | checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" 663 | 664 | [[package]] 665 | name = "unicode-ident" 666 | version = "1.0.2" 667 | source = "registry+https://github.com/rust-lang/crates.io-index" 668 | checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7" 669 | 670 | [[package]] 671 | name = "unicode-normalization" 672 | version = "0.1.21" 673 | source = "registry+https://github.com/rust-lang/crates.io-index" 674 | checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6" 675 | dependencies = [ 676 | "tinyvec", 677 | ] 678 | 679 | [[package]] 680 | name = "url" 681 | version = "2.2.2" 682 | source = "registry+https://github.com/rust-lang/crates.io-index" 683 | checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" 684 | dependencies = [ 685 | "form_urlencoded", 686 | "idna", 687 | "matches", 688 | "percent-encoding", 689 | ] 690 | 691 | [[package]] 692 | name = "version_check" 693 | version = "0.9.4" 694 | source = "registry+https://github.com/rust-lang/crates.io-index" 695 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 696 | 697 | [[package]] 698 | name = "wasi" 699 | version = "0.11.0+wasi-snapshot-preview1" 700 | source = "registry+https://github.com/rust-lang/crates.io-index" 701 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 702 | 703 | [[package]] 704 | name = "winapi" 705 | version = "0.3.9" 706 | source = "registry+https://github.com/rust-lang/crates.io-index" 707 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 708 | dependencies = [ 709 | "winapi-i686-pc-windows-gnu", 710 | "winapi-x86_64-pc-windows-gnu", 711 | ] 712 | 713 | [[package]] 714 | name = "winapi-i686-pc-windows-gnu" 715 | version = "0.4.0" 716 | source = "registry+https://github.com/rust-lang/crates.io-index" 717 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 718 | 719 | [[package]] 720 | name = "winapi-util" 721 | version = "0.1.5" 722 | source = "registry+https://github.com/rust-lang/crates.io-index" 723 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 724 | dependencies = [ 725 | "winapi", 726 | ] 727 | 728 | [[package]] 729 | name = "winapi-x86_64-pc-windows-gnu" 730 | version = "0.4.0" 731 | source = "registry+https://github.com/rust-lang/crates.io-index" 732 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 733 | 734 | [[package]] 735 | name = "windows-sys" 736 | version = "0.36.1" 737 | source = "registry+https://github.com/rust-lang/crates.io-index" 738 | checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" 739 | dependencies = [ 740 | "windows_aarch64_msvc", 741 | "windows_i686_gnu", 742 | "windows_i686_msvc", 743 | "windows_x86_64_gnu", 744 | "windows_x86_64_msvc", 745 | ] 746 | 747 | [[package]] 748 | name = "windows_aarch64_msvc" 749 | version = "0.36.1" 750 | source = "registry+https://github.com/rust-lang/crates.io-index" 751 | checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" 752 | 753 | [[package]] 754 | name = "windows_i686_gnu" 755 | version = "0.36.1" 756 | source = "registry+https://github.com/rust-lang/crates.io-index" 757 | checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" 758 | 759 | [[package]] 760 | name = "windows_i686_msvc" 761 | version = "0.36.1" 762 | source = "registry+https://github.com/rust-lang/crates.io-index" 763 | checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" 764 | 765 | [[package]] 766 | name = "windows_x86_64_gnu" 767 | version = "0.36.1" 768 | source = "registry+https://github.com/rust-lang/crates.io-index" 769 | checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" 770 | 771 | [[package]] 772 | name = "windows_x86_64_msvc" 773 | version = "0.36.1" 774 | source = "registry+https://github.com/rust-lang/crates.io-index" 775 | checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" 776 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["rope-bench"] 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Eric Zhang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | redis-rope 3 |

4 | 5 | A fast and versatile [rope]() data type for large strings in [Redis](https://redis.io), distributed as a native [module](https://redis.io/docs/reference/modules/). 6 | 7 | ## Overview 8 | 9 | Ropes are a more efficient data structure for large strings (indexed sequences of bytes). Unlike ordinary strings, ropes let you do some operations up to exponentially faster than their counterparts: 10 | 11 | - **Add bytes** to the beginning, middle, or end — any index you want. 12 | - **Delete any rope substring** or move it to a different position within the rope. 13 | - **Splice / concatenate any substring** of a rope with any other rope. 14 | - **Read any substring** with random access. 15 | 16 | The ropes in this module are backed by [splay trees](https://en.wikipedia.org/wiki/Splay_tree), which are a self-adjusting data structure that has logarithmic amortized worst-case performance, while recently-accessed indices are also quick to access in subsequent operations. Each splay tree node stores between 64 and 127 bytes of data. 17 | 18 | ### Design 19 | 20 | Some data structures tend to be too theoretical. This module attempts to provide practical guarantees: 21 | 22 | - **The memory usage of a rope is proportional to its length.** It must be a small constant factor more than the number of bytes stored. (Data is stored in chunks; the constant varies based on fragmentation.) 23 | - **All operations should be fast in practice.** We aim to approach the speed of ordinary strings for simple operations and to be hundreds of times faster for complex operations. 24 | - **This module never panics.** If a memory allocation fails, it exits gracefully with an error. The database will never be left in a partially modified or inconsistent state. 25 | - **Stack size is limited and should not overflow.** No operations on arbitrary trees are implemented recursively. We do not create unbounded stack buffers. 26 | - **Micro-optimizations are not accepted if they make the code less clear.** Safety and correctness is paramount, and code needs to be easily understood by the reader. 27 | 28 | ### Example / Benchmark 29 | 30 | Ropes are particularly good at speeding up complex operations on large strings. The following graph shows how performance for ropes scales on 1000 random string SPLICE operations, compared to an equivalent implementation with ordinary Redis strings. (These operations are pipelined to better measure their CPU performance; see the [benchmark code in Rust](rope-bench/src/main.rs).) 31 | 32 |

33 | Latency graph comparing redis-rope and native Redis strings 34 |

35 | 36 | For small strings, there is not much difference. However, each time the length of the string doubles, the basic type gets exponentially slower because it does not scale to large data as well, while the `redis-rope` type provided by this module stays fast. 37 | 38 | ## Installation 39 | 40 | The `redis-rope` module has been tested with Redis 7.0+. To install, download the appropriate shared library `libredisrope.so` for your platform and load the module from the command line: 41 | 42 | ```sh-session 43 | redis-server --loadmodule path/to/libredisrope.so 44 | ``` 45 | 46 | Or by configuration directive in `redis.conf`: 47 | 48 | ``` 49 | loadmodule path/to/libredisrope.so 50 | ``` 51 | 52 | Or from the Redis CLI, using the `MODULE LOAD` command: 53 | 54 | ``` 55 | > MODULE LOAD path/to/libredisrope.so 56 | ``` 57 | 58 | ### Prebuilt binaries 59 | 60 | We will build shared libraries for each version of redis-rope on Linux and macOS, using x86-64 and ARM64 architectures. These files are small, portable artifacts and are available on the [releases page](https://github.com/ekzhang/redis-rope/releases). 61 | 62 | ### Building from source 63 | 64 | `redis-rope` is written in Zig, which makes building the module from source and cross-compiling very fast (<10 seconds). This is a reasonable option, especially if you want to try out the latest version of the module from the main branch. 65 | 66 | ``` 67 | zig build -Drelease-fast 68 | ``` 69 | 70 | This requires Zig 0.9, which you can install [here](https://ziglang.org/download/). The project can also be built targeting different platforms with a command-line flag, for example: 71 | 72 | ``` 73 | zig build -Drelease-fast -Dtarget=x86_64-linux-gnu 74 | zig build -Drelease-fast -Dtarget=aarch64-linux-gnu 75 | ``` 76 | 77 | Build outputs are located in the `zig-out/lib` folder. 78 | 79 | ## Commands 80 | 81 | ### Read operations 82 | 83 | These are fairly straightfoward: get the length of the rope, any individual byte, or a range of bytes as a string. 84 | 85 | - `ROPE.LEN` _key_: **O(1)** 86 | - `ROPE.GET` _key_ _index_: **O(log N)** 87 | - `ROPE.GETRANGE` _key_ _start_ _stop_: **O(log N + K)**, where K is the length of the returned string 88 | 89 | All operations support negative indices, which count backward from the end of the rope. 90 | 91 | ### Write operations 92 | 93 | The append and insert operations push data to the end of the rope, or at an index in the middle of the rope, while the delrange operation deletes a byte range from the rope. 94 | 95 | The splice operation is the most complicated and powerful. Given the keys of two ropes, `source` and `destination`, it appends `destination` to the end of `source` and deletes `destination`. If `start` is provided, the string is inserted at that index rather than appended to the end. If `stop` is provided, then the range of bytes from `start` to `stop` is also deleted from `source` and swapped with the rope at `destination`. 96 | 97 | - `ROPE.APPEND` _key_ _str_: **O(1)** 98 | - `ROPE.INSERT` _key_ _index_ _str_: **O(log N)**, or **O(1)** if _index_ is 0 99 | - `ROPE.DELRANGE` _key_ _start_ _stop_: **O(log N)** 100 | - `ROPE.SPLICE` _source_ _destination_ [_start_ \[_stop_\]]: **O(log N)** 101 | 102 | Despite being quite powerful, each operation above takes logarithmic time, so they will remain fast for arbitrarily long ropes. 103 | 104 | ### Other operations 105 | 106 | The rope data type supports exact calculations from the `MEMORY USAGE` command, both methods of [Redis persistence](https://redis.io/docs/manual/persistence/) using RDB and AOF, asynchronous `DEL` operations, and primary-replica replication. 107 | 108 | ## Example usage 109 | 110 | ```scala 111 | redis:6379> ROPE.APPEND key1 "hello" 112 | (integer) 5 113 | redis:6379> ROPE.LEN key1 114 | (integer) 5 115 | redis:6379> ROPE.GET key1 2 116 | "l" 117 | redis:6379> ROPE.APPEND key1 " world!" 118 | (integer) 12 119 | redis:6379> ROPE.GETRANGE key1 0 -1 120 | "hello world!" 121 | redis:6379> ROPE.INSERT key1 6 "rope " 122 | (integer) 17 123 | redis:6379> ROPE.GETRANGE key1 0 -1 124 | "hello rope world!" 125 | redis:6379> ROPE.DELRANGE key1 -9 -3 126 | (integer) 10 127 | redis:6379> ROPE.GETRANGE key1 0 -1 128 | "hello rod!" 129 | redis:6379> ROPE.APPEND key2 "goodbye" 130 | (integer) 7 131 | redis:6379> ROPE.SPLICE key1 key2 0 4 132 | 1) (integer) 12 133 | 2) (integer) 5 134 | redis:6379> ROPE.GETRANGE key1 0 -1 135 | "goodbye rod!" 136 | redis:6379> ROPE.GETRANGE key2 0 -1 137 | "hello" 138 | redis:6379> ROPE.SPLICE key1 key2 139 | 1) (integer) 17 140 | 2) (integer) 0 141 | redis:6379> ROPE.GETRANGE key1 0 -1 142 | "goodbye rod!hello" 143 | redis:6379> MEMORY USAGE key1 144 | (integer) 128 145 | redis:6379> GET key2 146 | (nil) 147 | redis:6379> DEL key1 148 | (integer) 1 149 | redis:6379> GET key1 150 | (nil) 151 | ``` 152 | 153 | ## Acknowledgements 154 | 155 | Created by Eric Zhang ([@ekzhang1](https://twitter.com/ekzhang1)). Licensed under the [MIT license](LICENSE). 156 | 157 | Thanks to [antirez](http://antirez.com/) for creating Redis and [Sleator & Tarjan](https://www.cs.cmu.edu/~sleator/papers/self-adjusting.pdf) for discovering splay trees. 158 | -------------------------------------------------------------------------------- /build.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn build(b: *std.build.Builder) void { 4 | // Standard release options allow the person running `zig build` to select 5 | // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. 6 | const mode = b.standardReleaseOptions(); 7 | 8 | // e.g., for Linux, use -Dtarget=x86_64-linux-gnu 9 | const target = b.standardTargetOptions(.{}); 10 | 11 | const lib = b.addSharedLibrary("redisrope", "src/main.zig", .unversioned); 12 | lib.setBuildMode(mode); 13 | lib.setTarget(target); 14 | lib.linkLibC(); 15 | lib.install(); 16 | 17 | const main_tests = b.addTest("src/test.zig"); 18 | main_tests.setBuildMode(mode); 19 | 20 | const test_step = b.step("test", "Run library tests"); 21 | test_step.dependOn(&main_tests.step); 22 | } 23 | -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | sharedlib := if os() == "macos" { "libredisrope.dylib" } else { "libredisrope.so" } 2 | 3 | build: 4 | zig build -Drelease-fast 5 | 6 | build-artifacts: 7 | mkdir -p zig-out/artifacts 8 | zig build -Drelease-fast -Dtarget=x86_64-linux-gnu && \ 9 | mv zig-out/lib/libredisrope.so zig-out/artifacts/libredisrope-x86_64-linux-gnu.so 10 | zig build -Drelease-fast -Dtarget=x86_64-linux-musl && \ 11 | mv zig-out/lib/libredisrope.so zig-out/artifacts/libredisrope-x86_64-linux-musl.so 12 | zig build -Drelease-fast -Dtarget=aarch64-linux-gnu && \ 13 | mv zig-out/lib/libredisrope.so zig-out/artifacts/libredisrope-aarch64-linux-gnu.so 14 | zig build -Drelease-fast -Dtarget=aarch64-linux-musl && \ 15 | mv zig-out/lib/libredisrope.so zig-out/artifacts/libredisrope-aarch64-linux-musl.so 16 | zig build -Drelease-fast -Dtarget=x86_64-macos-gnu && \ 17 | mv zig-out/lib/libredisrope.dylib zig-out/artifacts/libredisrope-x86_64-macos-gnu.dylib 18 | zig build -Drelease-fast -Dtarget=aarch64-macos-gnu && \ 19 | mv zig-out/lib/libredisrope.dylib zig-out/artifacts/libredisrope-aarch64-macos-gnu.dylib 20 | 21 | test: 22 | zig build test 23 | 24 | server: build 25 | redis-server --loadmodule zig-out/lib/{{sharedlib}} --enable-debug-command local 26 | 27 | bench: build 28 | cargo run --release zig-out/lib/{{sharedlib}} 29 | 30 | bench-quiet: build 31 | cargo run --release --quiet zig-out/lib/{{sharedlib}} --quiet 32 | -------------------------------------------------------------------------------- /rope-bench/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rope-bench" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | ansi_term = "0.12.1" 9 | anyhow = "1.0.58" 10 | clap = { version = "3.2.14", features = ["derive"] } 11 | indoc = "1.0.6" 12 | nix = "0.24.2" 13 | rand = "0.8.5" 14 | redis = { version = "0.21.5", features = ["aio", "tokio-comp"] } 15 | tokio = { version = "1.20.0", features = ["full"] } 16 | -------------------------------------------------------------------------------- /rope-bench/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::future::Future; 2 | use std::path::PathBuf; 3 | use std::process::{ExitStatus, Stdio}; 4 | use std::sync::{Arc, Mutex}; 5 | 6 | use ansi_term::Color::{Blue, Green, Red, Yellow}; 7 | use ansi_term::Style; 8 | use anyhow::{ensure, Result}; 9 | use clap::Parser; 10 | use indoc::formatdoc; 11 | use nix::sys::signal::{self, Signal}; 12 | use nix::unistd::Pid; 13 | use rand::{rngs::StdRng, Rng, SeedableRng}; 14 | use redis::{AsyncCommands, Client, Cmd, Script, ScriptInvocation, ToRedisArgs}; 15 | use tokio::fs; 16 | use tokio::io::AsyncWriteExt; 17 | use tokio::process::{Child, Command}; 18 | use tokio::time::{self, Duration, Instant}; 19 | 20 | #[derive(Parser)] 21 | #[clap(about, long_about = None)] 22 | /// A fast benchmarking harness for the redis-rope module. 23 | struct Args { 24 | /// Path to the redisrope module shared library. 25 | #[clap(value_parser)] 26 | module_path: PathBuf, 27 | 28 | /// Unix domain socket for redis connections. 29 | #[clap(short, long, value_parser, default_value = "/tmp/redis.sock")] 30 | socket: PathBuf, 31 | 32 | /// Set to hide output from the Redis server. 33 | #[clap(short, long)] 34 | quiet: bool, 35 | } 36 | 37 | /// Spawns a redis server at the location. 38 | async fn spawn_server(args: &Args) -> Result { 39 | fs::remove_file(&args.socket).await.ok(); 40 | 41 | let get_output = || { 42 | if args.quiet { 43 | Stdio::piped() 44 | } else { 45 | Stdio::inherit() 46 | } 47 | }; 48 | let mut child = Command::new("redis-server") 49 | .arg("-") 50 | .stdin(Stdio::piped()) 51 | .stdout(get_output()) 52 | .stderr(get_output()) 53 | .kill_on_drop(true) 54 | .spawn()?; 55 | { 56 | let mut stdin = child.stdin.take().unwrap(); 57 | let options = formatdoc! {" 58 | save \"\" 59 | dbfilename \"\" 60 | port 0 61 | unixsocket {} 62 | loadmodule {}", 63 | args.socket.display(), 64 | args.module_path.display(), 65 | }; 66 | stdin.write_all(options.as_bytes()).await?; 67 | } 68 | 69 | // Wait for redis to start. 70 | while fs::metadata(&args.socket).await.is_err() { 71 | time::sleep(Duration::from_millis(5)).await; 72 | } 73 | 74 | Ok(child) 75 | } 76 | 77 | /// Sends a termination signal to a child process and waits for it. 78 | async fn terminate(mut child: Child) -> Result { 79 | signal::kill(Pid::from_raw(child.id().unwrap() as i32), Signal::SIGTERM)?; 80 | Ok(child.wait().await?) 81 | } 82 | 83 | /// Retrieves the name of a function. 84 | fn function_name(_: &T) -> &'static str { 85 | std::any::type_name::() 86 | } 87 | 88 | /// A timer for critical sections of code. 89 | #[derive(Clone, Default)] 90 | struct Timer { 91 | inner: Arc, Option)>>, 92 | } 93 | 94 | impl Timer { 95 | pub fn new() -> Self { 96 | Default::default() 97 | } 98 | 99 | pub fn start(&self) { 100 | self.inner.lock().unwrap().0 = Some(Instant::now()); 101 | } 102 | 103 | pub fn stop(&self) { 104 | let mut values = self.inner.lock().unwrap(); 105 | if let Some(ts) = values.0 { 106 | values.1 = Some(ts.elapsed()); 107 | } 108 | } 109 | 110 | pub fn get(&self) -> Option { 111 | self.inner.lock().unwrap().1 112 | } 113 | } 114 | 115 | async fn run_test(client: &Client, func: F) -> Result<()> 116 | where 117 | F: Fn(Client, Timer) -> Fut, 118 | Fut: Future>, 119 | { 120 | let name = function_name(&func); 121 | print!("{name} ... "); 122 | 123 | let timer = Timer::new(); 124 | let start = Instant::now(); 125 | let result = func(client.clone(), timer.clone()).await; 126 | 127 | let status = match &result { 128 | Ok(()) => Green.paint("ok!"), 129 | Err(_) => Red.paint("ERR"), 130 | }; 131 | let dimmed = Style::new().dimmed(); 132 | let duration = format!("({:?})", start.elapsed()); 133 | let duration = dimmed.paint(duration); 134 | println!("{status} {duration}"); 135 | if let Some(duration) = timer.get() { 136 | let text = format!("critical section: {:?}", duration); 137 | println!(" {} {}", dimmed.paint("└──"), Yellow.paint(text)); 138 | } 139 | 140 | result 141 | } 142 | 143 | fn cmd(cmd: &str) -> Cmd { 144 | let mut iter = cmd.split(' '); 145 | let start = iter.next().expect("command is empty"); 146 | let mut builder = redis::cmd(start); 147 | for arg in iter { 148 | builder.arg(arg); 149 | } 150 | builder 151 | } 152 | 153 | fn inv( 154 | script: &Script, 155 | keys: impl IntoIterator, 156 | args: impl IntoIterator, 157 | ) -> ScriptInvocation { 158 | let mut builder = script.prepare_invoke(); 159 | for key in keys { 160 | builder.key(key); 161 | } 162 | for arg in args { 163 | builder.arg(arg); 164 | } 165 | builder 166 | } 167 | 168 | async fn basic_ops(client: Client, _: Timer) -> Result<()> { 169 | let conn = &mut client.get_async_connection().await?; 170 | 171 | conn.set("hello", "world").await?; 172 | ensure!(conn.get::<_, String>("hello").await? == "world"); 173 | conn.del("hello").await?; 174 | 175 | Ok(()) 176 | } 177 | 178 | async fn rope_len(client: Client, _: Timer) -> Result<()> { 179 | let conn = &mut client.get_async_connection().await?; 180 | 181 | let result: i64 = cmd("ROPE.LEN hello").query_async(conn).await?; 182 | ensure!(result == 0); 183 | 184 | conn.set("hello", "world").await?; 185 | let result = cmd("ROPE.LEN hello").query_async::<_, i32>(conn).await; 186 | ensure!(result.is_err()); 187 | ensure!(result.unwrap_err().to_string().contains("WRONGTYPE")); 188 | conn.del("hello").await?; 189 | 190 | Ok(()) 191 | } 192 | 193 | async fn manipulation(client: Client, _: Timer) -> Result<()> { 194 | let conn = &mut client.get_async_connection().await?; 195 | 196 | cmd("ROPE.APPEND a foobar").query_async(conn).await?; 197 | cmd("ROPE.INSERT a 3 baz").query_async(conn).await?; 198 | let s: String = cmd("ROPE.GETRANGE a 0 -1").query_async(conn).await?; 199 | ensure!(s == "foobazbar"); 200 | let x: i64 = cmd("ROPE.LEN a").query_async(conn).await?; 201 | ensure!(x == 9); 202 | for (i, r) in [(-2, Some("a")), (-200, None), (5, Some("z")), (9, None)] { 203 | let code = format!("ROPE.GET a {i}"); 204 | let c: Option = cmd(&code).query_async(conn).await?; 205 | ensure!(c.as_deref() == r); 206 | } 207 | let x: i64 = cmd("ROPE.DELRANGE a 6 2").query_async(conn).await?; 208 | ensure!(x == 0); 209 | let x: i64 = cmd("ROPE.DELRANGE a 0 -1000").query_async(conn).await?; 210 | ensure!(x == 0); 211 | let x: i64 = cmd("ROPE.DELRANGE a 6 -4").query_async(conn).await?; 212 | ensure!(x == 0); 213 | let s: String = cmd("ROPE.GETRANGE a 0 -1").query_async(conn).await?; 214 | ensure!(s == "foobazbar"); 215 | let x: i64 = cmd("ROPE.DELRANGE a -5 5").query_async(conn).await?; 216 | ensure!(x == 2); 217 | let s: String = cmd("ROPE.GETRANGE a 0 -1").query_async(conn).await?; 218 | ensure!(s == "foobbar"); 219 | let x: i64 = cmd("ROPE.DELRANGE a 0 6").query_async(conn).await?; 220 | ensure!(x == 7); 221 | assert!(conn.get::<_, Option>("a").await?.is_none()); 222 | 223 | Ok(()) 224 | } 225 | 226 | async fn append_64mb(client: Client, _: Timer) -> Result<()> { 227 | let conn = &mut client.get_async_connection().await?; 228 | 229 | let append_cmd = cmd(&format!("ROPE.APPEND a {}", "1234567890123456".repeat(64))); 230 | let mut pipe = redis::pipe(); 231 | for _ in 0..65536 { 232 | pipe.add_command(append_cmd.clone()).ignore(); 233 | } 234 | pipe.add_command(cmd("ROPE.LEN a")); 235 | let (result,): (i64,) = pipe.query_async(conn).await?; 236 | ensure!(result == 2_i64.pow(26)); 237 | conn.del("a").await?; 238 | 239 | Ok(()) 240 | } 241 | 242 | async fn append_64mb_str(client: Client, _: Timer) -> Result<()> { 243 | let conn = &mut client.get_async_connection().await?; 244 | 245 | let append_cmd = cmd(&format!("APPEND a {}", "1234567890123456".repeat(64))); 246 | let mut pipe = redis::pipe(); 247 | for _ in 0..65536 { 248 | pipe.add_command(append_cmd.clone()).ignore(); 249 | } 250 | pipe.add_command(cmd("STRLEN a")); 251 | let (result,): (i64,) = pipe.query_async(conn).await?; 252 | ensure!(result == 2_i64.pow(26)); 253 | conn.del("a").await?; 254 | 255 | Ok(()) 256 | } 257 | 258 | async fn splicer_1m(client: Client, timer: Timer) -> Result<()> { 259 | let conn = &mut client.get_async_connection().await?; 260 | 261 | let text = "abcd".repeat(262144); 262 | let len = text.len() as u64; 263 | cmd(&format!("ROPE.APPEND a {text}")) 264 | .query_async(conn) 265 | .await?; 266 | 267 | let mut rng = StdRng::from_seed([1; 32]); 268 | let mut pipe = redis::pipe(); 269 | let mut current_len = 0; 270 | for _ in 0..1000 { 271 | let start = rng.gen_range(0..len - current_len); 272 | let end = rng.gen_range(start..len - current_len); 273 | pipe.add_command(cmd(&format!("ROPE.SPLICE a b {start} {end}"))) 274 | .ignore(); 275 | current_len = end - start + 1; 276 | } 277 | timer.start(); 278 | pipe.query_async(conn).await?; 279 | timer.stop(); 280 | 281 | let res1: String = cmd("ROPE.GETRANGE a 0 -1").query_async(conn).await?; 282 | let res2: String = cmd("ROPE.GETRANGE b 0 -1").query_async(conn).await?; 283 | let res = res1 + &res2; 284 | for c in "abcd".chars() { 285 | // Ensure that characters were preserved. 286 | ensure!(res.matches(c).count() == text.matches(c).count()); 287 | } 288 | conn.del(&["a", "b"]).await?; 289 | 290 | Ok(()) 291 | } 292 | 293 | async fn splicer_1m_str(client: Client, timer: Timer) -> Result<()> { 294 | const SCRIPT_CODE: &str = r#" 295 | local src = redis.call('GET', KEYS[1]) or '' 296 | local dest = redis.call('GET', KEYS[2]) or '' 297 | redis.call('SET', KEYS[2], string.sub(src, ARGV[1], ARGV[2])) 298 | redis.call('SET', KEYS[1], string.sub(src, 1, ARGV[1] - 1) .. dest .. string.sub(src, ARGV[2] + 1)) 299 | return redis.status_reply('OK') 300 | "#; 301 | 302 | let conn = &mut client.get_async_connection().await?; 303 | let script = Script::new(SCRIPT_CODE); 304 | let hash = script.get_hash(); 305 | 306 | let first_call = inv(&script, ["a", "b"], [1, 2]); 307 | ensure!(first_call.invoke_async::<_, ()>(conn).await.is_ok()); // prepare 308 | 309 | let text = "abcd".repeat(262144); 310 | let len = text.len() as u64; 311 | cmd(&format!("SET a {text}")).query_async(conn).await?; 312 | 313 | let mut rng = StdRng::from_seed([1; 32]); 314 | let mut pipe = redis::pipe(); 315 | let mut current_len = 0; 316 | for _ in 0..1000 { 317 | let start = rng.gen_range(0..len - current_len) + 1; 318 | let end = rng.gen_range(start..=len - current_len); 319 | pipe.add_command(cmd(&format!("EVALSHA {hash} 2 a b {start} {end}"))) 320 | .ignore(); 321 | current_len = end - start + 1; 322 | } 323 | timer.start(); 324 | pipe.query_async(conn).await?; 325 | timer.stop(); 326 | 327 | let (res1, res2): (String, String) = conn.get(&["a", "b"]).await?; 328 | let res = res1 + &res2; 329 | for c in "abcd".chars() { 330 | // Ensure that characters were preserved. 331 | ensure!(res.matches(c).count() == text.matches(c).count()); 332 | } 333 | conn.del(&["a", "b"]).await?; 334 | 335 | Ok(()) 336 | } 337 | 338 | #[tokio::main] 339 | async fn main() -> Result<()> { 340 | let args = Args::parse(); 341 | 342 | let child = spawn_server(&args).await?; 343 | 344 | { 345 | let uri = format!("redis+unix:///{}", args.socket.display()); 346 | let client = Client::open(uri)?; 347 | 348 | println!("{}", Blue.paint("------ STARTING TESTS ------")); 349 | run_test(&client, basic_ops).await?; 350 | run_test(&client, rope_len).await?; 351 | run_test(&client, manipulation).await?; 352 | run_test(&client, append_64mb).await?; 353 | run_test(&client, append_64mb_str).await?; 354 | run_test(&client, splicer_1m).await?; 355 | run_test(&client, splicer_1m_str).await?; 356 | println!("{}", Blue.paint("----- ALL TESTS PASSED -----")); 357 | } 358 | 359 | terminate(child).await?; 360 | Ok(()) 361 | } 362 | -------------------------------------------------------------------------------- /src/cmd.zig: -------------------------------------------------------------------------------- 1 | //! Defines commands exported by the module. 2 | 3 | const std = @import("std"); 4 | 5 | const rm = @import("vendor/redismodule.zig"); 6 | 7 | const interop = @import("interop.zig"); 8 | const RedisError = interop.RedisError; 9 | const Rope = @import("rope.zig").Rope; 10 | const ReservingAllocator = @import("reserve.zig").ReservingAllocator; 11 | 12 | pub var reserving_allocator = ReservingAllocator(6).init(interop.allocator); 13 | const allocator = reserving_allocator.allocator(); 14 | 15 | comptime { 16 | if (Rope.node_size == Rope.rope_size) { 17 | @compileError("Rope.node_size == Rope.rope_size, which breaks the reserving allocator"); 18 | } 19 | } 20 | 21 | /// The type of a rope, initialized at module load time. 22 | pub var rope_type: *rm.RedisModuleType = undefined; 23 | 24 | /// Type methods defining the rope type. 25 | pub var rope_tm: rm.RedisModuleTypeMethods = .{ 26 | .version = rm.REDISMODULE_TYPE_METHOD_VERSION, 27 | .rdb_load = ropeRdbLoad, 28 | .rdb_save = ropeRdbSave, 29 | .aof_rewrite = ropeAofRewrite, 30 | .free = ropeFree, 31 | 32 | // Optional fields 33 | .digest = ropeDigest, 34 | .mem_usage = ropeMemUsage, 35 | .free_effort = ropeFreeEffort, 36 | }; 37 | 38 | export fn ropeRdbLoad(io: *rm.RedisModuleIO, encver: c_int) ?*anyopaque { 39 | if (encver != 0) { 40 | // Can't load data with this version number. 41 | rm.RedisModule_LogIOError(io, "warning", "Can't load data with version %d", encver); 42 | return null; 43 | } 44 | const size = rm.RedisModule_LoadUnsigned(io); 45 | const bytes = allocator.alloc(u8, size) catch return null; 46 | defer allocator.free(bytes); 47 | 48 | const blocks = rm.RedisModule_LoadUnsigned(io); 49 | var i: u64 = 0; 50 | var cursor: u64 = 0; 51 | while (i < blocks) : (i += 1) { 52 | var len: u64 = undefined; 53 | if (rm.RedisModule_LoadStringBuffer(io, &len)) |ptr| { 54 | std.mem.copy(u8, bytes[cursor..], ptr[0..len]); 55 | cursor += len; 56 | rm.RedisModule_Free(ptr); 57 | } else return null; 58 | } 59 | std.debug.assert(cursor == size); 60 | return Rope.create(allocator, bytes) catch null; 61 | } 62 | 63 | export fn ropeRdbSave(io: *rm.RedisModuleIO, value: *anyopaque) void { 64 | const rope = @ptrCast(*Rope, @alignCast(@alignOf(*Rope), value)); 65 | const size = rope.len(); 66 | rm.RedisModule_SaveUnsigned(io, size); 67 | var chunks = rope.chunks(0, size); 68 | rm.RedisModule_SaveUnsigned(io, chunks.remaining()); 69 | while (chunks.next()) |buf| { 70 | rm.RedisModule_SaveStringBuffer(io, buf.ptr, buf.len); 71 | } 72 | } 73 | 74 | export fn ropeAofRewrite(io: *rm.RedisModuleIO, key: *rm.RedisModuleString, value: *anyopaque) void { 75 | const rope = @ptrCast(*Rope, @alignCast(@alignOf(*Rope), value)); 76 | var chunks = rope.chunks(0, rope.len()); 77 | while (chunks.next()) |buf| { 78 | rm.RedisModule_EmitAOF(io, "ROPE.APPEND", "sb", key, buf.ptr, buf.len); 79 | } 80 | } 81 | 82 | export fn ropeFree(value: *anyopaque) void { 83 | const rope = @ptrCast(*Rope, @alignCast(@alignOf(*Rope), value)); 84 | rope.destroy(); 85 | } 86 | 87 | export fn ropeDigest(md: *rm.RedisModuleDigest, value: *anyopaque) void { 88 | const rope = @ptrCast(*Rope, @alignCast(@alignOf(*Rope), value)); 89 | var chunks = rope.chunks(0, rope.len()); 90 | while (chunks.next()) |buf| { 91 | rm.RedisModule_DigestAddStringBuffer(md, buf.ptr, buf.len); 92 | } 93 | rm.RedisModule_DigestEndSequence(md); 94 | } 95 | 96 | export fn ropeMemUsage(value: *const anyopaque) usize { 97 | const rope = @ptrCast(*const Rope, @alignCast(@alignOf(*Rope), value)); 98 | return @intCast(usize, rope.memusage()); 99 | } 100 | 101 | export fn ropeFreeEffort(_: *rm.RedisModuleString, value: *const anyopaque) usize { 102 | const rope = @ptrCast(*const Rope, @alignCast(@alignOf(*Rope), value)); 103 | return @intCast(usize, rope.numnodes() + 1); 104 | } 105 | 106 | /// Check that a key for a rope type has a nonempty value and fetch it. 107 | fn readKey(key: *rm.RedisModuleKey) !?*Rope { 108 | if (rm.RedisModule_KeyType(key) != rm.REDISMODULE_KEYTYPE_EMPTY) { 109 | if (rm.RedisModule_ModuleTypeGetType(key) != rope_type) { 110 | return RedisError.WrongType; 111 | } 112 | const value = rm.RedisModule_ModuleTypeGetValue(key); 113 | return @ptrCast(*Rope, @alignCast(@alignOf(*Rope), value)); 114 | } 115 | return null; 116 | } 117 | 118 | /// Set the value of an empty key to a rope type. 119 | fn setKey(key: *rm.RedisModuleKey, rope: *Rope) void { 120 | const result = rm.RedisModule_ModuleTypeSetValue(key, rope_type, rope); 121 | std.debug.assert(result == rm.REDISMODULE_OK); // should be opened for writing 122 | } 123 | 124 | /// Normalize an index, possibly negative, and clamp it to the length boundary. 125 | fn getIndex(index: i64, len: u64) u64 { 126 | if (index < 0) { 127 | const neg = std.math.absCast(index); 128 | return len -| neg; 129 | } 130 | return std.math.min(@intCast(u64, index), len); 131 | } 132 | 133 | /// Similar to getIndex, but this handles inclusive end indices. 134 | fn getEndIndex(index: i64, len: u64) u64 { 135 | if (index < 0) { 136 | const neg = std.math.absCast(index) - 1; 137 | return len -| neg; 138 | } 139 | return std.math.min(@intCast(u64, index) + 1, len); 140 | } 141 | 142 | /// Gets the length of a rope in bytes. 143 | pub fn ropeLen(ctx: *rm.RedisModuleCtx, args: []*rm.RedisModuleString) !void { 144 | if (args.len != 2) return RedisError.Arity; 145 | const key = rm.RedisModule_OpenKey(ctx, args[1], rm.REDISMODULE_READ); 146 | const len = if (try readKey(key)) |rope| rope.len() else 0; 147 | interop.replyInt(ctx, len); 148 | } 149 | 150 | /// Read a single byte character of a rope. 151 | pub fn ropeGet(ctx: *rm.RedisModuleCtx, args: []*rm.RedisModuleString) !void { 152 | if (args.len != 3) return RedisError.Arity; 153 | const key = rm.RedisModule_OpenKey(ctx, args[1], rm.REDISMODULE_READ); 154 | var index = try interop.strToIndex(args[2]); 155 | 156 | var result: ?u8 = null; 157 | if (try readKey(key)) |rope| { 158 | const len = rope.len(); 159 | if (index < 0) 160 | index += @intCast(i64, len); 161 | if (index >= 0) 162 | result = rope.get(@intCast(u64, index)); 163 | } 164 | 165 | if (result) |c| { 166 | interop.replyString(ctx, &.{c}); 167 | } else { 168 | interop.replyNull(ctx); 169 | } 170 | } 171 | 172 | /// Read a range of bytes from a rope. 173 | pub fn ropeGetRange(ctx: *rm.RedisModuleCtx, args: []*rm.RedisModuleString) !void { 174 | if (args.len != 4) return RedisError.Arity; 175 | const key = rm.RedisModule_OpenKey(ctx, args[1], rm.REDISMODULE_READ); 176 | var start = try interop.strToIndex(args[2]); 177 | var end = try interop.strToIndex(args[3]); 178 | 179 | if (try readKey(key)) |rope| { 180 | const len = rope.len(); 181 | std.debug.assert(len > 0); 182 | const s = getIndex(start, len); 183 | const e = getEndIndex(end, len); 184 | if (s < e) { 185 | var slice: []u8 = undefined; 186 | var chunks = rope.chunks(s, e); 187 | if (chunks.remaining() == 1) { 188 | slice = chunks.next().?; 189 | std.debug.assert(chunks.next() == null); 190 | } else { 191 | slice = try allocator.alloc(u8, e - s); 192 | defer allocator.free(slice); 193 | var cursor: u64 = 0; 194 | while (chunks.next()) |buf| { 195 | std.mem.copy(u8, slice[cursor..], buf[0..]); 196 | cursor += buf.len; 197 | } 198 | std.debug.assert(cursor == slice.len); 199 | } 200 | interop.replyString(ctx, slice); 201 | return; 202 | } 203 | } 204 | 205 | interop.replyNull(ctx); 206 | } 207 | 208 | /// Append a string onto the end of a rope. 209 | pub fn ropeAppend(ctx: *rm.RedisModuleCtx, args: []*rm.RedisModuleString) !void { 210 | if (args.len != 3) return RedisError.Arity; 211 | const key = rm.RedisModule_OpenKey(ctx, args[1], rm.REDISMODULE_READ | rm.REDISMODULE_WRITE); 212 | const bytes = interop.strToSlice(args[2]); 213 | 214 | const rope2 = try Rope.create(allocator, bytes); 215 | errdefer rope2.destroy(); 216 | std.debug.assert(rope2.len() == bytes.len); 217 | 218 | if (try readKey(key)) |rope| { 219 | try rope.merge(rope2); 220 | interop.replyInt(ctx, rope.len()); 221 | } else { 222 | setKey(key, rope2); 223 | interop.replyInt(ctx, bytes.len); 224 | } 225 | _ = rm.RedisModule_ReplicateVerbatim(ctx); 226 | } 227 | 228 | /// Insert a string at a specific index in a rope. 229 | pub fn ropeInsert(ctx: *rm.RedisModuleCtx, args: []*rm.RedisModuleString) !void { 230 | if (args.len != 4) return RedisError.Arity; 231 | const key = rm.RedisModule_OpenKey(ctx, args[1], rm.REDISMODULE_READ | rm.REDISMODULE_WRITE); 232 | const index = try interop.strToIndex(args[2]); 233 | const bytes = interop.strToSlice(args[3]); 234 | 235 | const rope2 = try Rope.create(allocator, bytes); 236 | errdefer rope2.destroy(); 237 | std.debug.assert(rope2.len() == bytes.len); 238 | 239 | if (try readKey(key)) |rope| { 240 | const i = getIndex(index, rope.len()); 241 | if (i == 0) { 242 | try rope2.merge(rope); 243 | rope.* = rope2.*; 244 | } else if (i == rope.len()) { 245 | try rope.merge(rope2); 246 | } else { 247 | try reserving_allocator.ensure(Rope.node_size, 3); 248 | const rope3 = try rope.split(i); 249 | // These are infallible due to the earlier reservation. Each merge 250 | // operation allocates at most one Node, and the split operation 251 | // allocates at most 1 Node and one Rope. 252 | rope.merge(rope2) catch unreachable; 253 | rope.merge(rope3) catch unreachable; 254 | } 255 | interop.replyInt(ctx, rope.len()); 256 | } else { 257 | setKey(key, rope2); 258 | interop.replyInt(ctx, bytes.len); 259 | } 260 | _ = rm.RedisModule_ReplicateVerbatim(ctx); 261 | } 262 | 263 | /// Delete a range of bytes from a rope. 264 | pub fn ropeDelRange(ctx: *rm.RedisModuleCtx, args: []*rm.RedisModuleString) !void { 265 | if (args.len != 4) return RedisError.Arity; 266 | const key = rm.RedisModule_OpenKey(ctx, args[1], rm.REDISMODULE_READ | rm.REDISMODULE_WRITE); 267 | var start = try interop.strToIndex(args[2]); 268 | var end = try interop.strToIndex(args[3]); 269 | 270 | var removed_bytes: u64 = 0; 271 | if (try readKey(key)) |rope| { 272 | const len = rope.len(); 273 | std.debug.assert(len > 0); 274 | const s = getIndex(start, len); 275 | const e = getEndIndex(end, len); 276 | if (s == 0 and e == len) { 277 | // Special case: Delete the entire rope. 278 | _ = rm.RedisModule_UnlinkKey(key); 279 | removed_bytes = len; 280 | } else if (s < e) { 281 | try reserving_allocator.ensure(Rope.node_size, 3); 282 | try reserving_allocator.ensure(Rope.rope_size, 2); 283 | // Each split operation allocates at most one Node and one Rope, 284 | // and the merge operation allocates at most one Node. 285 | const rope2 = try rope.split(s); 286 | const rope3 = rope2.split(e - s) catch unreachable; 287 | rope.merge(rope3) catch unreachable; 288 | removed_bytes = e - s; 289 | 290 | if (std.Thread.spawn(.{ .stack_size = 8192 }, Rope.destroy, .{rope2})) |thread| { 291 | thread.detach(); 292 | } else |_| { 293 | // In the rare case that spawning a background thread fails, we fall 294 | // back to freeing the rope's memory synchronously. 295 | rope2.destroy(); 296 | } 297 | } 298 | } 299 | interop.replyInt(ctx, removed_bytes); 300 | _ = rm.RedisModule_ReplicateVerbatim(ctx); 301 | } 302 | 303 | /// Swap the bytes of the subrange of a rope with another rope. 304 | pub fn ropeSplice(ctx: *rm.RedisModuleCtx, args: []*rm.RedisModuleString) !void { 305 | if (args.len < 3 or args.len > 5) return RedisError.Arity; 306 | const key_src = rm.RedisModule_OpenKey(ctx, args[1], rm.REDISMODULE_READ | rm.REDISMODULE_WRITE); 307 | const key_dest = rm.RedisModule_OpenKey(ctx, args[2], rm.REDISMODULE_READ | rm.REDISMODULE_WRITE); 308 | var start = if (args.len > 3) try interop.strToIndex(args[3]) else null; 309 | var end = if (args.len > 4) try interop.strToIndex(args[4]) else null; 310 | 311 | var src_len: u64 = 0; 312 | var dest_len: u64 = 0; 313 | if (try readKey(key_src)) |rope| { 314 | const len = rope.len(); 315 | std.debug.assert(len > 0); 316 | const s = if (start) |i| getIndex(i, len) else len; 317 | const e = if (end) |i| getEndIndex(i, len) else 0; 318 | 319 | if (try readKey(key_dest)) |rope2| { 320 | if (s < e) { 321 | try reserving_allocator.ensure(Rope.node_size, 4); 322 | try reserving_allocator.ensure(Rope.rope_size, 2); 323 | 324 | const rope2_other = try rope.split(s); 325 | const rope3 = rope2_other.split(e - s) catch unreachable; 326 | std.mem.swap(Rope, rope2, rope2_other); 327 | rope.merge(rope2_other) catch unreachable; 328 | rope.merge(rope3) catch unreachable; 329 | 330 | dest_len = rope2.len(); 331 | } else { 332 | // Temporary tree to move rope2 out of its attachment to key_dest. 333 | const rope2_temp = try Rope.create(allocator, &.{}); 334 | try reserving_allocator.ensure(Rope.node_size, 3); 335 | 336 | const rope3 = try rope.split(s); 337 | std.mem.swap(Rope, rope2, rope2_temp); 338 | _ = rm.RedisModule_DeleteKey(key_dest); 339 | rope.merge(rope2_temp) catch unreachable; 340 | rope.merge(rope3) catch unreachable; 341 | } 342 | std.debug.assert(rope.len() > 0); 343 | src_len = rope.len(); 344 | } else if (s < e) { 345 | try reserving_allocator.ensure(Rope.node_size, 3); 346 | try reserving_allocator.ensure(Rope.rope_size, 2); 347 | 348 | const rope2 = try rope.split(s); 349 | const rope3 = rope2.split(e - s) catch unreachable; 350 | rope.merge(rope3) catch unreachable; 351 | 352 | setKey(key_dest, rope2); 353 | src_len = rope.len(); 354 | dest_len = rope2.len(); 355 | if (rope.len() == 0) _ = rm.RedisModule_DeleteKey(key_src); 356 | } 357 | } else if (try readKey(key_dest)) |rope2| { 358 | const rope = try Rope.create(allocator, &.{}); 359 | std.mem.swap(Rope, rope, rope2); 360 | setKey(key_src, rope); 361 | _ = rm.RedisModule_DeleteKey(key_dest); // Should be an empty rope anyway. 362 | src_len = rope.len(); 363 | } 364 | interop.replyArray(ctx, 2); 365 | interop.replyInt(ctx, src_len); 366 | interop.replyInt(ctx, dest_len); 367 | _ = rm.RedisModule_ReplicateVerbatim(ctx); 368 | } 369 | -------------------------------------------------------------------------------- /src/interop.zig: -------------------------------------------------------------------------------- 1 | //! Interop helpers for the C redismodule header file. 2 | 3 | const std = @import("std"); 4 | const Allocator = std.mem.Allocator; 5 | 6 | const rm = @import("vendor/redismodule.zig"); 7 | 8 | /// An error set that can be returned from Redis operations. 9 | pub const RedisError = error{ Arity, WrongType, OutOfMemory, BadIndex, SetValue }; 10 | 11 | /// Convert a RedisError union to the corresponding message reply. 12 | fn finishCommand(ctx: *rm.RedisModuleCtx, result: RedisError!void) c_int { 13 | return if (result) rm.REDISMODULE_OK else |err| switch (err) { 14 | RedisError.Arity => rm.RedisModule_WrongArity(ctx), 15 | RedisError.WrongType => rm.RedisModule_ReplyWithError(ctx, rm.REDISMODULE_ERRORMSG_WRONGTYPE), 16 | RedisError.OutOfMemory => rm.RedisModule_ReplyWithError(ctx, "OOM out of memory, allocation failed"), 17 | RedisError.BadIndex => rm.RedisModule_ReplyWithError(ctx, "ERR index was not a valid integer"), 18 | RedisError.SetValue => rm.RedisModule_ReplyWithError(ctx, "ERR failed to set value for key"), 19 | }; 20 | } 21 | 22 | /// Define a new Redis command, wrapping some boilerplate. 23 | pub fn redisCommand(comptime func: fn (*rm.RedisModuleCtx, []*rm.RedisModuleString) RedisError!void) rm.RedisModuleCmdFunc { 24 | return struct { 25 | fn command(ctx: *rm.RedisModuleCtx, argv: [*c]*rm.RedisModuleString, argc: c_int) callconv(.C) c_int { 26 | rm.RedisModule_AutoMemory(ctx); 27 | const args = argv[0..@intCast(usize, argc)]; 28 | return finishCommand(ctx, func(ctx, args)); 29 | } 30 | }.command; 31 | } 32 | 33 | /// Convert a `RedisModuleString` argument to an integer index. 34 | pub fn strToIndex(str: *const rm.RedisModuleString) RedisError!i64 { 35 | var num: i64 = undefined; 36 | if (rm.RedisModule_StringToLongLong(str, &num) == rm.REDISMODULE_ERR) { 37 | return RedisError.BadIndex; 38 | } 39 | return num; 40 | } 41 | 42 | /// Fetches the underlying byte slice for a Redis string. 43 | pub fn strToSlice(str: *const rm.RedisModuleString) []const u8 { 44 | var len: usize = undefined; 45 | const ptr = rm.RedisModule_StringPtrLen(str, &len); 46 | return ptr[0..len]; 47 | } 48 | 49 | pub fn replyNull(ctx: *rm.RedisModuleCtx) void { 50 | _ = rm.RedisModule_ReplyWithNull(ctx); 51 | } 52 | pub fn replyInt(ctx: *rm.RedisModuleCtx, int: anytype) void { 53 | _ = rm.RedisModule_ReplyWithLongLong(ctx, @intCast(c_longlong, int)); 54 | } 55 | pub fn replyString(ctx: *rm.RedisModuleCtx, string: []u8) void { 56 | _ = rm.RedisModule_ReplyWithStringBuffer(ctx, string.ptr, string.len); 57 | } 58 | pub fn replyArray(ctx: *rm.RedisModuleCtx, len: u64) void { 59 | _ = rm.RedisModule_ReplyWithArray(ctx, @intCast(c_long, len)); 60 | } 61 | 62 | /// A memory allocator that uses the `RedisModule_*` memory allocation functions. 63 | pub const allocator = Allocator{ 64 | .ptr = undefined, 65 | .vtable = &allocator_vtable, 66 | }; 67 | 68 | /// Functions based on the implementation of `std.heap.raw_c_allocator`. 69 | const allocator_vtable = Allocator.VTable{ 70 | .alloc = redisAlloc, 71 | .resize = redisResize, 72 | .free = redisFree, 73 | }; 74 | 75 | fn redisAlloc( 76 | _: *anyopaque, 77 | len: usize, 78 | alignment: u29, 79 | len_align: u29, 80 | ret_addr: usize, 81 | ) ![]u8 { 82 | _ = len_align; 83 | _ = ret_addr; 84 | std.debug.assert(alignment <= @alignOf(std.c.max_align_t)); 85 | const start = rm.RedisModule_TryAlloc(len) orelse return error.OutOfMemory; 86 | const ptr = @ptrCast([*]u8, start); 87 | return ptr[0..len]; 88 | } 89 | 90 | fn redisResize( 91 | _: *anyopaque, 92 | buf: []u8, 93 | buf_align: u29, 94 | new_len: usize, 95 | len_align: u29, 96 | ret_addr: usize, 97 | ) ?usize { 98 | _ = buf_align; 99 | _ = ret_addr; 100 | if (new_len <= buf.len) { 101 | return std.mem.alignAllocLen(buf.len, new_len, len_align); 102 | } 103 | const full_len = rm.RedisModule_MallocSize(buf.ptr); 104 | if (new_len <= full_len) { 105 | return std.mem.alignAllocLen(full_len, new_len, len_align); 106 | } 107 | return null; 108 | } 109 | 110 | fn redisFree( 111 | _: *anyopaque, 112 | buf: []u8, 113 | buf_align: u29, 114 | ret_addr: usize, 115 | ) void { 116 | _ = buf_align; 117 | _ = ret_addr; 118 | rm.RedisModule_Free(buf.ptr); 119 | } 120 | -------------------------------------------------------------------------------- /src/main.zig: -------------------------------------------------------------------------------- 1 | //! Library implementing the redis-rope data type, which provides flexible byte 2 | //! sequences based on splay trees. 3 | 4 | const rm = @import("vendor/redismodule.zig"); 5 | 6 | const interop = @import("interop.zig"); 7 | const cmd = @import("cmd.zig"); 8 | 9 | export const RopeLen_Command = interop.redisCommand(cmd.ropeLen); 10 | export const RopeGet_Command = interop.redisCommand(cmd.ropeGet); 11 | export const RopeGetRange_Command = interop.redisCommand(cmd.ropeGetRange); 12 | export const RopeAppend_Command = interop.redisCommand(cmd.ropeAppend); 13 | export const RopeInsert_Command = interop.redisCommand(cmd.ropeInsert); 14 | export const RopeDelRange_Command = interop.redisCommand(cmd.ropeDelRange); 15 | export const RopeSplice_Command = interop.redisCommand(cmd.ropeSplice); 16 | 17 | export fn RedisModule_OnLoad(ctx: *rm.RedisModuleCtx) c_int { 18 | if (rm.RedisModule_Init(ctx, "redisrope", 1, rm.REDISMODULE_APIVER_1) == rm.REDISMODULE_ERR) { 19 | return rm.REDISMODULE_ERR; 20 | } 21 | 22 | if (@ptrCast(?*const anyopaque, rm.RedisModule_TryAlloc) == null) { 23 | rm.RedisModule_Log(ctx, "warning", "FATAL: Redis version is too old, does not support TryAlloc"); 24 | return rm.REDISMODULE_ERR; 25 | } 26 | 27 | if (rm.RedisModule_CreateDataType(ctx, "Rope--ekz", 0, &cmd.rope_tm)) |ty| { 28 | cmd.rope_type = ty; 29 | } else { 30 | return rm.REDISMODULE_ERR; 31 | } 32 | 33 | const commands = .{ 34 | .{ "rope.len", RopeLen_Command, "readonly fast", 1, 1, 1 }, 35 | .{ "rope.get", RopeGet_Command, "readonly fast", 1, 1, 1 }, 36 | .{ "rope.getrange", RopeGetRange_Command, "readonly", 1, 1, 1 }, 37 | .{ "rope.append", RopeAppend_Command, "write deny-oom fast", 1, 1, 1 }, 38 | .{ "rope.insert", RopeInsert_Command, "write deny-oom fast", 1, 1, 1 }, 39 | .{ "rope.delrange", RopeDelRange_Command, "write fast", 1, 1, 1 }, 40 | .{ "rope.splice", RopeSplice_Command, "write deny-oom fast", 1, 2, 1 }, 41 | }; 42 | 43 | inline for (commands) |c| { 44 | if (rm.RedisModule_CreateCommand(ctx, c[0], c[1], c[2], c[3], c[4], c[5]) == rm.REDISMODULE_ERR) { 45 | return rm.REDISMODULE_ERR; 46 | } 47 | } 48 | 49 | return rm.REDISMODULE_OK; 50 | } 51 | 52 | export fn RedisModule_OnUnload(ctx: *rm.RedisModuleCtx) void { 53 | _ = ctx; 54 | cmd.reserving_allocator.deinit(); 55 | } 56 | -------------------------------------------------------------------------------- /src/reserve.zig: -------------------------------------------------------------------------------- 1 | //! Memory reservations for ensuring safety in complex operations. 2 | 3 | const std = @import("std"); 4 | const Allocator = std.mem.Allocator; 5 | const Mutex = std.Thread.Mutex; 6 | 7 | /// An allocator that can reserve memory allocations before fallible multi-step 8 | /// operations, ensuring that they do not have an OOM in the middle. 9 | /// 10 | /// This wraps a containing allocator, propagates errors, and is thread-safe. 11 | pub fn ReservingAllocator(comptime capacity: usize) type { 12 | return struct { 13 | const Self = @This(); 14 | 15 | /// The allocator that is used to actually allocate memory. 16 | inner: Allocator, 17 | /// Guards the fields below. 18 | mutex: Mutex = .{}, 19 | /// A list of reserved memory entries. 20 | entries: std.BoundedArray([]u8, capacity) = .{ .buffer = undefined }, 21 | 22 | pub fn init(inner: Allocator) Self { 23 | return .{ .inner = inner }; 24 | } 25 | 26 | pub fn deinit(self: *Self) void { 27 | while (self.entries.popOrNull()) |entry| { 28 | self.inner.rawFree(entry, 0, 0); 29 | } 30 | } 31 | 32 | pub fn allocator(self: *Self) Allocator { 33 | return Allocator.init(self, alloc, resize, free); 34 | } 35 | 36 | /// Ensures the allocator can handle `count` more allocations of size `len`. 37 | pub fn ensure(self: *Self, len: usize, count: usize) !void { 38 | self.mutex.lock(); 39 | defer self.mutex.unlock(); 40 | 41 | var existing: usize = 0; 42 | for (self.entries.slice()) |entry| { 43 | if (entry.len == len) 44 | existing += 1; 45 | } 46 | if (existing < count) { 47 | var extra = count - existing; 48 | self.entries.ensureUnusedCapacity(extra) catch return error.OutOfMemory; 49 | while (extra > 0) { 50 | extra -= 1; 51 | const buf = try self.inner.rawAlloc(len, @alignOf(std.c.max_align_t), 0, 0); 52 | self.entries.appendAssumeCapacity(buf); 53 | } 54 | } 55 | } 56 | 57 | fn allocReserved(self: *Self, len: usize) ?[]u8 { 58 | self.mutex.lock(); 59 | defer self.mutex.unlock(); 60 | 61 | for (self.entries.slice()) |entry, i| { 62 | if (entry.len == len) 63 | return self.entries.swapRemove(i); 64 | } 65 | return null; 66 | } 67 | 68 | fn alloc(self: *Self, len: usize, alignment: u29, len_align: u29, ret_addr: usize) ![]u8 { 69 | std.debug.assert(alignment <= @alignOf(std.c.max_align_t)); 70 | if (self.allocReserved(len)) |buf| { 71 | return buf; 72 | } 73 | return self.inner.rawAlloc(len, alignment, len_align, ret_addr); 74 | } 75 | 76 | fn resize(self: *Self, buf: []u8, buf_align: u29, new_len: usize, len_align: u29, ret_addr: usize) ?usize { 77 | return self.inner.rawResize(buf, buf_align, new_len, len_align, ret_addr); 78 | } 79 | 80 | fn free(self: *Self, buf: []u8, buf_align: u29, ret_addr: usize) void { 81 | self.inner.rawFree(buf, buf_align, ret_addr); 82 | } 83 | }; 84 | } 85 | -------------------------------------------------------------------------------- /src/rope.zig: -------------------------------------------------------------------------------- 1 | //! A rope data structure based on splay trees, optimized to use less memory. 2 | 3 | const std = @import("std"); 4 | const Allocator = std.mem.Allocator; 5 | 6 | /// The minimum number of bytes stored in a splay tree node. 7 | const min_bytes = 64; 8 | 9 | /// The capacity of a splay tree node in bytes. 10 | const cap_bytes = 127; 11 | 12 | comptime { 13 | if (min_bytes < 1 or min_bytes * 2 != cap_bytes + 1) { 14 | @compileError("min_bytes must be half of cap_bytes + 1"); 15 | } 16 | if (cap_bytes >= 128) { 17 | @compileError("cap_bytes must be less than 128 to do arithmetic"); 18 | } 19 | } 20 | 21 | /// A node in the splay tree. 22 | const Node = struct { 23 | parent: ?*Node = null, 24 | child: [2]?*Node = .{ null, null }, 25 | nodes: u64 = 1, 26 | size: u64 = 0, 27 | len: u8 = 0, 28 | data: [cap_bytes]u8 = undefined, 29 | 30 | fn dir(self: *const Node) u1 { 31 | return if (self.parent) |p| @boolToInt(p.child[1] == self) else 0; 32 | } 33 | 34 | fn update(self: *Node) void { 35 | self.size = self.len; 36 | self.nodes = 1; 37 | for (self.child) |ch| if (ch) |c| { 38 | self.size += c.size; 39 | self.nodes += c.nodes; 40 | }; 41 | } 42 | 43 | fn connect(pa: ?*Node, ch: ?*Node, x: u1) void { 44 | if (ch) |c| c.parent = pa; 45 | if (pa) |p| p.child[x] = ch; 46 | } 47 | 48 | fn rot(self: *Node) void { 49 | std.debug.assert(self.parent != null); 50 | 51 | const x = self.dir(); 52 | const pa = self.parent.?; 53 | 54 | connect(pa.parent, self, pa.dir()); 55 | connect(pa, self.child[x ^ 1], x); 56 | connect(self, pa, x ^ 1); 57 | 58 | pa.update(); 59 | self.update(); 60 | } 61 | 62 | /// Run the splay operation on this node, bringing it to the root. 63 | fn splay(self: *Node) void { 64 | while (self.parent != null and self.parent.?.parent != null) { 65 | if (self.dir() == self.parent.?.dir()) { 66 | self.parent.?.rot(); 67 | } else { 68 | self.rot(); 69 | } 70 | self.rot(); 71 | } 72 | if (self.parent != null) { 73 | self.rot(); 74 | } 75 | std.debug.assert(self.parent == null); 76 | } 77 | 78 | /// Free a splay tree node without using recursion. 79 | fn destroy(self: *Node, allocator: Allocator) void { 80 | var node = self; 81 | while (true) { 82 | if (node.child[0]) |c| { 83 | node.child[0] = null; 84 | node = c; 85 | } else if (node.child[1]) |c| { 86 | node.child[1] = null; 87 | node = c; 88 | } else { 89 | const next = node.parent; 90 | allocator.destroy(node); 91 | node = next orelse break; 92 | } 93 | } 94 | } 95 | }; 96 | 97 | /// Access a splay tree at an index, returning the new root. 98 | fn access(node: *Node, index: u64) *Node { 99 | std.debug.assert(index < node.size); 100 | var n = node; 101 | var i = index; 102 | while (true) { 103 | const left_size = if (n.child[0]) |c| c.size else 0; 104 | if (i < left_size) { 105 | n = n.child[0].?; 106 | } else { 107 | i -= left_size; 108 | if (i < n.len) { 109 | n.splay(); 110 | return n; 111 | } else { 112 | i -= n.len; 113 | n = n.child[1].?; 114 | } 115 | } 116 | } 117 | } 118 | 119 | /// Allocate a pre-balanced tree of nodes from a data slice. 120 | fn createTree(allocator: Allocator, data: []const u8) Allocator.Error!*Node { 121 | std.debug.assert(data.len >= min_bytes); 122 | var node: *Node = undefined; 123 | if (data.len <= cap_bytes) { 124 | node = try allocator.create(Node); 125 | node.* = .{ .len = @intCast(u8, data.len) }; 126 | std.mem.copy(u8, node.data[0..], data); 127 | } else { 128 | const blocks = data.len / cap_bytes; 129 | if (blocks < 2) { 130 | // node 131 | // \ 132 | // right 133 | node = try createTree(allocator, data[0 .. data.len / 2]); 134 | errdefer node.destroy(allocator); 135 | std.debug.assert(node.child[1] == null); 136 | const right = try createTree(allocator, data[data.len / 2 ..]); 137 | Node.connect(node, right, 1); 138 | } else if (blocks < 3) { 139 | // node 140 | // / \ 141 | // left ... 142 | node = try createTree(allocator, data[cap_bytes..]); 143 | errdefer node.destroy(allocator); 144 | std.debug.assert(node.child[0] == null); 145 | const left = try createTree(allocator, data[0..cap_bytes]); 146 | Node.connect(node, left, 0); 147 | } else { 148 | // node 149 | // / \ 150 | // left right 151 | const start_idx = (blocks / 2) * cap_bytes; 152 | node = try createTree(allocator, data[start_idx .. start_idx + cap_bytes]); 153 | errdefer node.destroy(allocator); 154 | std.debug.assert(node.child[0] == null); 155 | std.debug.assert(node.child[1] == null); 156 | const left = try createTree(allocator, data[0..start_idx]); 157 | errdefer left.destroy(allocator); 158 | const right = try createTree(allocator, data[start_idx + cap_bytes ..]); 159 | Node.connect(node, left, 0); 160 | Node.connect(node, right, 1); 161 | } 162 | } 163 | node.update(); 164 | return node; 165 | } 166 | 167 | /// Utility method for concatenating a slice to the front of another. 168 | fn concat_front(dest: []u8, src: []u8) void { 169 | std.debug.assert(dest.len >= src.len); 170 | var i = dest.len - src.len; 171 | while (i > 0) { 172 | i -= 1; 173 | dest[i + src.len] = dest[i]; 174 | } 175 | std.mem.copy(u8, dest[0..], src); 176 | } 177 | 178 | pub const Rope = struct { 179 | pub const rope_size = @sizeOf(Rope); 180 | pub const node_size = @sizeOf(Node); 181 | 182 | allocator: Allocator, 183 | root: ?*Node = null, 184 | suf_len: u8 = 0, 185 | suf_buf: [min_bytes - 1]u8 = undefined, 186 | 187 | /// Create a new, pre-balanced rope from a byte slice. 188 | pub fn create(allocator: Allocator, bytes: []const u8) !*Rope { 189 | const rope = try allocator.create(Rope); 190 | errdefer allocator.destroy(rope); 191 | rope.* = .{ .allocator = allocator }; 192 | 193 | // Use only a suffix if the rope is too small. 194 | if (bytes.len < min_bytes) { 195 | rope.suf_len = @intCast(u8, bytes.len); 196 | std.mem.copy(u8, rope.suf_buf[0..], bytes); 197 | return rope; 198 | } 199 | rope.root = try createTree(allocator, bytes); 200 | return rope; 201 | } 202 | 203 | /// Free the memory used by a rope and its nodes. 204 | pub fn destroy(self: *Rope) void { 205 | if (self.root) |node| { 206 | node.destroy(self.allocator); 207 | } 208 | self.allocator.destroy(self); 209 | } 210 | 211 | pub fn len(self: *const Rope) u64 { 212 | return self.suf_len + if (self.root) |r| r.size else 0; 213 | } 214 | 215 | pub fn empty(self: *const Rope) bool { 216 | return self.len() == 0; 217 | } 218 | 219 | /// Merge this rope with another rope, taking ownership of it. 220 | /// 221 | /// On out-of-memory error, this function is safe. Neither rope is changed 222 | /// in a way that semantically modifies the values in it. 223 | pub fn merge(self: *Rope, other: *Rope) !void { 224 | std.debug.assert(self.allocator.vtable == other.allocator.vtable); 225 | 226 | if (other.root == null) { 227 | // Concatenate a small buffer onto the end of self. 228 | const total = self.suf_len + other.suf_len; 229 | if (total < min_bytes) { 230 | std.mem.copy(u8, self.suf_buf[self.suf_len..], other.suf_buf[0..other.suf_len]); 231 | self.suf_len = total; 232 | } else { 233 | std.debug.assert(total <= cap_bytes); 234 | const node = try self.allocator.create(Node); 235 | node.* = .{ .len = total }; 236 | std.mem.copy(u8, node.data[0..], self.suf_buf[0..self.suf_len]); 237 | std.mem.copy(u8, node.data[self.suf_len..], other.suf_buf[0..other.suf_len]); 238 | Node.connect(node, self.root, 0); 239 | node.update(); 240 | self.root = node; 241 | self.suf_len = 0; 242 | } 243 | } else { 244 | // Concatenate our suffix with the start of the other rope's root. 245 | _ = other.get(0); // splay 246 | var root = other.root.?; 247 | std.debug.assert(root.child[0] == null); 248 | const total = root.len + self.suf_len; 249 | if (total < cap_bytes) { 250 | root.len += self.suf_len; 251 | concat_front(root.data[0..root.len], self.suf_buf[0..self.suf_len]); 252 | } else { 253 | std.debug.assert(root.len >= min_bytes); 254 | const node = try self.allocator.create(Node); 255 | node.* = .{ .len = min_bytes }; 256 | 257 | std.mem.copy(u8, node.data[0..], self.suf_buf[0..self.suf_len]); 258 | std.mem.copy(u8, node.data[self.suf_len..], root.data[0 .. min_bytes - self.suf_len]); 259 | std.mem.copy(u8, root.data[0..], root.data[min_bytes - self.suf_len .. root.len]); 260 | root.len -= min_bytes - self.suf_len; 261 | root.update(); 262 | 263 | Node.connect(node, root, 1); 264 | root = node; 265 | } 266 | std.debug.assert(root.child[0] == null); 267 | Node.connect(root, self.root, 0); 268 | self.root = null; 269 | root.update(); 270 | 271 | other.root = root; 272 | std.mem.swap(Rope, self, other); 273 | } 274 | other.destroy(); 275 | } 276 | 277 | /// Splits this rope into two at the given index. 278 | /// 279 | /// The current rope will be the first part, and the second part starting at 280 | /// and including the index will be returned as a new rope. 281 | /// 282 | /// On out-of-memory error, the rope is not modified. 283 | pub fn split(self: *Rope, index: u64) !*Rope { 284 | const length = self.len(); 285 | if (index > length) { 286 | // If the index is out-of-range, just return an empty rope. 287 | return try Rope.create(self.allocator, &.{}); 288 | } 289 | if (index >= length - self.suf_len) { 290 | const suf_rem = @intCast(u8, index - (length - self.suf_len)); 291 | const rope = try create(self.allocator, self.suf_buf[suf_rem..self.suf_len]); 292 | self.suf_len = suf_rem; 293 | return rope; 294 | } 295 | // The index is inside the splay tree. We split the tree now, although 296 | // it turns out that we never need to allocate new nodes here. 297 | std.debug.assert(index < self.root.?.size); 298 | const rope = try self.allocator.create(Rope); 299 | errdefer rope.destroy(); 300 | rope.* = .{ 301 | .allocator = self.allocator, 302 | .suf_len = self.suf_len, // copy suffix verbatim 303 | .suf_buf = self.suf_buf, // copy suffix verbatim 304 | }; 305 | 306 | _ = self.get(index); // splay 307 | const root = self.root.?; 308 | const left_len = if (root.child[0]) |c| c.size else 0; 309 | std.debug.assert(left_len <= index and index < left_len + root.len); 310 | const pivot = @intCast(u8, index - left_len); 311 | 312 | // Copy the left half of the node's data and establish the a new root. 313 | if (index - left_len >= min_bytes) { // doesn't fit in self.suf_buf 314 | const new_root = try self.allocator.create(Node); 315 | new_root.* = .{ .len = pivot }; 316 | std.mem.copy(u8, new_root.data[0..], root.data[0..pivot]); 317 | Node.connect(new_root, root.child[0], 0); 318 | new_root.update(); 319 | self.root = new_root; 320 | self.suf_len = 0; 321 | } else { // fits in self.suf_buf 322 | std.mem.copy(u8, self.suf_buf[0..], root.data[0..pivot]); 323 | self.suf_len = pivot; 324 | self.root = root.child[0]; 325 | if (self.root) |n| n.parent = null; 326 | } 327 | 328 | // Create the right half of the rope. First, we fix invariants. 329 | root.child[0] = null; 330 | std.mem.copy(u8, root.data[0..], root.data[pivot..root.len]); 331 | root.len -= pivot; 332 | root.update(); 333 | 334 | // We could just set `rope.root = root;` now, but we need to check and 335 | // fix one more invariant: each node has `len >= min_bytes`. 336 | if (root.len >= min_bytes) { 337 | rope.root = root; 338 | } else if (root.child[1]) |right_child| { 339 | // Splay the next inorder node to the top and do a left concatenation. 340 | right_child.parent = null; 341 | root.child[1] = null; 342 | const new_root = access(right_child, 0); 343 | std.debug.assert(new_root.child[0] == null); 344 | rope.root = new_root; 345 | if (root.len + new_root.len <= cap_bytes) { 346 | new_root.len += root.len; 347 | concat_front(new_root.data[0..new_root.len], root.data[0..root.len]); 348 | new_root.update(); 349 | root.destroy(self.allocator); 350 | } else { 351 | const copy_len = min_bytes - root.len; 352 | std.mem.copy(u8, root.data[root.len..], new_root.data[0..copy_len]); 353 | std.mem.copy(u8, new_root.data[0..], new_root.data[copy_len..new_root.len]); 354 | root.len += copy_len; 355 | new_root.len -= copy_len; 356 | Node.connect(new_root, root, 0); 357 | root.update(); 358 | new_root.update(); 359 | } 360 | } else if (rope.suf_len + root.len >= min_bytes) { 361 | // Concatenate the suffix onto the node directly. 362 | std.debug.assert(rope.suf_len + root.len <= cap_bytes); 363 | std.mem.copy(u8, root.data[root.len..], rope.suf_buf[0..rope.suf_len]); 364 | root.len += rope.suf_len; 365 | root.update(); 366 | rope.suf_len = 0; 367 | rope.root = root; 368 | } else { 369 | // Delete the root and only use the suffix buffer. 370 | rope.suf_len += root.len; 371 | concat_front(rope.suf_buf[0..rope.suf_len], root.data[0..root.len]); 372 | root.destroy(self.allocator); 373 | } 374 | 375 | return rope; 376 | } 377 | 378 | /// Get a byte of the rope. 379 | /// 380 | /// Note that splay trees have some really important mathematical properties 381 | /// here. For example, they have static optimality and are guaranteed to use 382 | /// only linear time when accessing nodes in inorder traversal. 383 | pub fn get(self: *Rope, i: u64) ?u8 { 384 | const slice = self.get_scan(i) orelse return null; 385 | return slice[0]; 386 | } 387 | 388 | /// Get a byte of the rope, also returning any remaining contiguous bytes. 389 | fn get_scan(self: *Rope, i: u64) ?[]u8 { 390 | const length = self.len(); 391 | if (i >= length) { 392 | return null; 393 | } else if (i >= length - self.suf_len) { 394 | return self.suf_buf[i - (length - self.suf_len) .. self.suf_len]; 395 | } else { 396 | var node = self.root.?; 397 | std.debug.assert(i < node.size); 398 | node = access(node, i); 399 | self.root = node; 400 | return node.data[i - (if (node.child[0]) |c| c.size else 0) .. node.len]; 401 | } 402 | } 403 | 404 | /// Return an efficient iterator over chunks in a range of bytes. 405 | pub fn chunks(self: *Rope, start: u64, end: u64) Chunks { 406 | std.debug.assert(start <= end and end <= self.len()); 407 | return .{ .rope = self, .start = start, .end = end }; 408 | } 409 | 410 | /// Returns the total memory usage of this data structure, in bytes. 411 | pub fn memusage(self: *const Rope) u64 { 412 | return rope_size + node_size * self.numnodes(); 413 | } 414 | 415 | /// Returns the total number of splay tree nodes in this data structure. 416 | pub fn numnodes(self: *const Rope) u64 { 417 | return if (self.root) |r| r.nodes else 0; 418 | } 419 | }; 420 | 421 | pub const Chunks = struct { 422 | const block_size = 65536; 423 | 424 | rope: *Rope, 425 | start: u64, 426 | end: u64, 427 | buf: [block_size]u8 = undefined, 428 | 429 | /// Count how many chunks are left in the iterator. 430 | pub fn remaining(self: *const Chunks) u64 { 431 | return std.math.divCeil(u64, self.end - self.start, block_size) catch unreachable; 432 | } 433 | 434 | /// Return the next chunk of this iterator, advancing the index. 435 | pub fn next(self: *Chunks) ?[]u8 { 436 | if (self.start >= self.end) { 437 | return null; 438 | } 439 | const len = std.math.min(self.end - self.start, block_size); 440 | var i: u64 = 0; 441 | while (i < len) { 442 | const slice = self.rope.get_scan(self.start + i).?; 443 | const k = std.math.min(slice.len, len - i); 444 | std.mem.copy(u8, self.buf[i..], slice[0..k]); 445 | i += k; 446 | } 447 | self.start += len; 448 | return self.buf[0..len]; 449 | } 450 | }; 451 | -------------------------------------------------------------------------------- /src/test.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const testing = std.testing; 3 | 4 | const Rope = @import("rope.zig").Rope; 5 | 6 | fn rope_eql(rope: *Rope, contents: []const u8) bool { 7 | if (rope.len() != contents.len) return false; 8 | for (contents) |c, i| { 9 | if (rope.get(i) != c) return false; 10 | } 11 | return true; 12 | } 13 | 14 | test "create a small rope" { 15 | const rope = try Rope.create(testing.allocator, "hello world!"); 16 | defer rope.destroy(); 17 | 18 | try testing.expect(rope.get(0) == @intCast(u8, 'h')); 19 | try testing.expect(rope.get(11) == @intCast(u8, '!')); 20 | try testing.expect(rope.get(12) == null); 21 | } 22 | 23 | test "create a larger rope" { 24 | const s = "abcdefghij" ** 15000; 25 | 26 | const rope = try Rope.create(testing.allocator, s); 27 | defer rope.destroy(); 28 | 29 | for (s) |c, i| { 30 | try testing.expect(rope.get(i) == c); 31 | } 32 | try testing.expect(rope.get(s.len) == null); 33 | } 34 | 35 | test "merge two ropes" { 36 | const s = "abcdefghij" ** 100; 37 | const t = "0123456789" ** 100; 38 | const st = s ++ t; 39 | 40 | const rope = try Rope.create(testing.allocator, s); 41 | defer rope.destroy(); 42 | const rope2 = try Rope.create(testing.allocator, t); 43 | 44 | rope.merge(rope2) catch |err| { 45 | rope2.destroy(); 46 | return err; 47 | }; 48 | 49 | for (st) |c, i| { 50 | try testing.expect(rope.get(i) == c); 51 | } 52 | try testing.expect(rope.get(st.len) == null); 53 | } 54 | 55 | test "split a small rope" { 56 | const rope = try Rope.create(testing.allocator, "applebanana"); 57 | defer rope.destroy(); 58 | const rope2 = try rope.split(5); 59 | defer rope2.destroy(); 60 | try testing.expect(rope_eql(rope, "apple")); 61 | try testing.expect(rope_eql(rope2, "banana")); 62 | } 63 | 64 | test "split and merge a large rope" { 65 | const bytes = "applebanana" ** 10000; 66 | const rope = try Rope.create(testing.allocator, bytes); 67 | defer rope.destroy(); 68 | 69 | var engine = std.rand.Xoshiro256.init(42); 70 | const rng = engine.random(); 71 | 72 | var i: u64 = 0; 73 | while (i < 20000) : (i += 1) { 74 | const split_index = rng.uintAtMost(u64, rope.len()); 75 | const rope2 = try rope.split(split_index); 76 | rope.merge(rope2) catch { 77 | rope2.destroy(); 78 | return; 79 | }; 80 | } 81 | 82 | try testing.expect(rope_eql(rope, bytes)); 83 | } 84 | -------------------------------------------------------------------------------- /src/vendor/redismodule.h: -------------------------------------------------------------------------------- 1 | #ifndef REDISMODULE_H 2 | #define REDISMODULE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /* ---------------- Defines common between core and modules --------------- */ 10 | 11 | /* Error status return values. */ 12 | #define REDISMODULE_OK 0 13 | #define REDISMODULE_ERR 1 14 | 15 | /* API versions. */ 16 | #define REDISMODULE_APIVER_1 1 17 | 18 | /* Version of the RedisModuleTypeMethods structure. Once the RedisModuleTypeMethods 19 | * structure is changed, this version number needs to be changed synchronistically. */ 20 | #define REDISMODULE_TYPE_METHOD_VERSION 4 21 | 22 | /* API flags and constants */ 23 | #define REDISMODULE_READ (1<<0) 24 | #define REDISMODULE_WRITE (1<<1) 25 | 26 | /* RedisModule_OpenKey extra flags for the 'mode' argument. 27 | * Avoid touching the LRU/LFU of the key when opened. */ 28 | #define REDISMODULE_OPEN_KEY_NOTOUCH (1<<16) 29 | 30 | /* List push and pop */ 31 | #define REDISMODULE_LIST_HEAD 0 32 | #define REDISMODULE_LIST_TAIL 1 33 | 34 | /* Key types. */ 35 | #define REDISMODULE_KEYTYPE_EMPTY 0 36 | #define REDISMODULE_KEYTYPE_STRING 1 37 | #define REDISMODULE_KEYTYPE_LIST 2 38 | #define REDISMODULE_KEYTYPE_HASH 3 39 | #define REDISMODULE_KEYTYPE_SET 4 40 | #define REDISMODULE_KEYTYPE_ZSET 5 41 | #define REDISMODULE_KEYTYPE_MODULE 6 42 | #define REDISMODULE_KEYTYPE_STREAM 7 43 | 44 | /* Reply types. */ 45 | #define REDISMODULE_REPLY_UNKNOWN -1 46 | #define REDISMODULE_REPLY_STRING 0 47 | #define REDISMODULE_REPLY_ERROR 1 48 | #define REDISMODULE_REPLY_INTEGER 2 49 | #define REDISMODULE_REPLY_ARRAY 3 50 | #define REDISMODULE_REPLY_NULL 4 51 | #define REDISMODULE_REPLY_MAP 5 52 | #define REDISMODULE_REPLY_SET 6 53 | #define REDISMODULE_REPLY_BOOL 7 54 | #define REDISMODULE_REPLY_DOUBLE 8 55 | #define REDISMODULE_REPLY_BIG_NUMBER 9 56 | #define REDISMODULE_REPLY_VERBATIM_STRING 10 57 | #define REDISMODULE_REPLY_ATTRIBUTE 11 58 | 59 | /* Postponed array length. */ 60 | #define REDISMODULE_POSTPONED_ARRAY_LEN -1 /* Deprecated, please use REDISMODULE_POSTPONED_LEN */ 61 | #define REDISMODULE_POSTPONED_LEN -1 62 | 63 | /* Expire */ 64 | #define REDISMODULE_NO_EXPIRE -1 65 | 66 | /* Sorted set API flags. */ 67 | #define REDISMODULE_ZADD_XX (1<<0) 68 | #define REDISMODULE_ZADD_NX (1<<1) 69 | #define REDISMODULE_ZADD_ADDED (1<<2) 70 | #define REDISMODULE_ZADD_UPDATED (1<<3) 71 | #define REDISMODULE_ZADD_NOP (1<<4) 72 | #define REDISMODULE_ZADD_GT (1<<5) 73 | #define REDISMODULE_ZADD_LT (1<<6) 74 | 75 | /* Hash API flags. */ 76 | #define REDISMODULE_HASH_NONE 0 77 | #define REDISMODULE_HASH_NX (1<<0) 78 | #define REDISMODULE_HASH_XX (1<<1) 79 | #define REDISMODULE_HASH_CFIELDS (1<<2) 80 | #define REDISMODULE_HASH_EXISTS (1<<3) 81 | #define REDISMODULE_HASH_COUNT_ALL (1<<4) 82 | 83 | #define REDISMODULE_CONFIG_DEFAULT 0 /* This is the default for a module config. */ 84 | #define REDISMODULE_CONFIG_IMMUTABLE (1ULL<<0) /* Can this value only be set at startup? */ 85 | #define REDISMODULE_CONFIG_SENSITIVE (1ULL<<1) /* Does this value contain sensitive information */ 86 | #define REDISMODULE_CONFIG_HIDDEN (1ULL<<4) /* This config is hidden in `config get ` (used for tests/debugging) */ 87 | #define REDISMODULE_CONFIG_PROTECTED (1ULL<<5) /* Becomes immutable if enable-protected-configs is enabled. */ 88 | #define REDISMODULE_CONFIG_DENY_LOADING (1ULL<<6) /* This config is forbidden during loading. */ 89 | 90 | #define REDISMODULE_CONFIG_MEMORY (1ULL<<7) /* Indicates if this value can be set as a memory value */ 91 | #define REDISMODULE_CONFIG_BITFLAGS (1ULL<<8) /* Indicates if this value can be set as a multiple enum values */ 92 | 93 | /* StreamID type. */ 94 | typedef struct RedisModuleStreamID { 95 | uint64_t ms; 96 | uint64_t seq; 97 | } RedisModuleStreamID; 98 | 99 | /* StreamAdd() flags. */ 100 | #define REDISMODULE_STREAM_ADD_AUTOID (1<<0) 101 | /* StreamIteratorStart() flags. */ 102 | #define REDISMODULE_STREAM_ITERATOR_EXCLUSIVE (1<<0) 103 | #define REDISMODULE_STREAM_ITERATOR_REVERSE (1<<1) 104 | /* StreamIteratorTrim*() flags. */ 105 | #define REDISMODULE_STREAM_TRIM_APPROX (1<<0) 106 | 107 | /* Context Flags: Info about the current context returned by 108 | * RM_GetContextFlags(). */ 109 | 110 | /* The command is running in the context of a Lua script */ 111 | #define REDISMODULE_CTX_FLAGS_LUA (1<<0) 112 | /* The command is running inside a Redis transaction */ 113 | #define REDISMODULE_CTX_FLAGS_MULTI (1<<1) 114 | /* The instance is a master */ 115 | #define REDISMODULE_CTX_FLAGS_MASTER (1<<2) 116 | /* The instance is a slave */ 117 | #define REDISMODULE_CTX_FLAGS_SLAVE (1<<3) 118 | /* The instance is read-only (usually meaning it's a slave as well) */ 119 | #define REDISMODULE_CTX_FLAGS_READONLY (1<<4) 120 | /* The instance is running in cluster mode */ 121 | #define REDISMODULE_CTX_FLAGS_CLUSTER (1<<5) 122 | /* The instance has AOF enabled */ 123 | #define REDISMODULE_CTX_FLAGS_AOF (1<<6) 124 | /* The instance has RDB enabled */ 125 | #define REDISMODULE_CTX_FLAGS_RDB (1<<7) 126 | /* The instance has Maxmemory set */ 127 | #define REDISMODULE_CTX_FLAGS_MAXMEMORY (1<<8) 128 | /* Maxmemory is set and has an eviction policy that may delete keys */ 129 | #define REDISMODULE_CTX_FLAGS_EVICT (1<<9) 130 | /* Redis is out of memory according to the maxmemory flag. */ 131 | #define REDISMODULE_CTX_FLAGS_OOM (1<<10) 132 | /* Less than 25% of memory available according to maxmemory. */ 133 | #define REDISMODULE_CTX_FLAGS_OOM_WARNING (1<<11) 134 | /* The command was sent over the replication link. */ 135 | #define REDISMODULE_CTX_FLAGS_REPLICATED (1<<12) 136 | /* Redis is currently loading either from AOF or RDB. */ 137 | #define REDISMODULE_CTX_FLAGS_LOADING (1<<13) 138 | /* The replica has no link with its master, note that 139 | * there is the inverse flag as well: 140 | * 141 | * REDISMODULE_CTX_FLAGS_REPLICA_IS_ONLINE 142 | * 143 | * The two flags are exclusive, one or the other can be set. */ 144 | #define REDISMODULE_CTX_FLAGS_REPLICA_IS_STALE (1<<14) 145 | /* The replica is trying to connect with the master. 146 | * (REPL_STATE_CONNECT and REPL_STATE_CONNECTING states) */ 147 | #define REDISMODULE_CTX_FLAGS_REPLICA_IS_CONNECTING (1<<15) 148 | /* THe replica is receiving an RDB file from its master. */ 149 | #define REDISMODULE_CTX_FLAGS_REPLICA_IS_TRANSFERRING (1<<16) 150 | /* The replica is online, receiving updates from its master. */ 151 | #define REDISMODULE_CTX_FLAGS_REPLICA_IS_ONLINE (1<<17) 152 | /* There is currently some background process active. */ 153 | #define REDISMODULE_CTX_FLAGS_ACTIVE_CHILD (1<<18) 154 | /* The next EXEC will fail due to dirty CAS (touched keys). */ 155 | #define REDISMODULE_CTX_FLAGS_MULTI_DIRTY (1<<19) 156 | /* Redis is currently running inside background child process. */ 157 | #define REDISMODULE_CTX_FLAGS_IS_CHILD (1<<20) 158 | /* The current client does not allow blocking, either called from 159 | * within multi, lua, or from another module using RM_Call */ 160 | #define REDISMODULE_CTX_FLAGS_DENY_BLOCKING (1<<21) 161 | /* The current client uses RESP3 protocol */ 162 | #define REDISMODULE_CTX_FLAGS_RESP3 (1<<22) 163 | /* Redis is currently async loading database for diskless replication. */ 164 | #define REDISMODULE_CTX_FLAGS_ASYNC_LOADING (1<<23) 165 | 166 | /* Next context flag, must be updated when adding new flags above! 167 | This flag should not be used directly by the module. 168 | * Use RedisModule_GetContextFlagsAll instead. */ 169 | #define _REDISMODULE_CTX_FLAGS_NEXT (1<<24) 170 | 171 | /* Keyspace changes notification classes. Every class is associated with a 172 | * character for configuration purposes. 173 | * NOTE: These have to be in sync with NOTIFY_* in server.h */ 174 | #define REDISMODULE_NOTIFY_KEYSPACE (1<<0) /* K */ 175 | #define REDISMODULE_NOTIFY_KEYEVENT (1<<1) /* E */ 176 | #define REDISMODULE_NOTIFY_GENERIC (1<<2) /* g */ 177 | #define REDISMODULE_NOTIFY_STRING (1<<3) /* $ */ 178 | #define REDISMODULE_NOTIFY_LIST (1<<4) /* l */ 179 | #define REDISMODULE_NOTIFY_SET (1<<5) /* s */ 180 | #define REDISMODULE_NOTIFY_HASH (1<<6) /* h */ 181 | #define REDISMODULE_NOTIFY_ZSET (1<<7) /* z */ 182 | #define REDISMODULE_NOTIFY_EXPIRED (1<<8) /* x */ 183 | #define REDISMODULE_NOTIFY_EVICTED (1<<9) /* e */ 184 | #define REDISMODULE_NOTIFY_STREAM (1<<10) /* t */ 185 | #define REDISMODULE_NOTIFY_KEY_MISS (1<<11) /* m (Note: This one is excluded from REDISMODULE_NOTIFY_ALL on purpose) */ 186 | #define REDISMODULE_NOTIFY_LOADED (1<<12) /* module only key space notification, indicate a key loaded from rdb */ 187 | #define REDISMODULE_NOTIFY_MODULE (1<<13) /* d, module key space notification */ 188 | #define REDISMODULE_NOTIFY_NEW (1<<14) /* n, new key notification */ 189 | 190 | /* Next notification flag, must be updated when adding new flags above! 191 | This flag should not be used directly by the module. 192 | * Use RedisModule_GetKeyspaceNotificationFlagsAll instead. */ 193 | #define _REDISMODULE_NOTIFY_NEXT (1<<15) 194 | 195 | #define REDISMODULE_NOTIFY_ALL (REDISMODULE_NOTIFY_GENERIC | REDISMODULE_NOTIFY_STRING | REDISMODULE_NOTIFY_LIST | REDISMODULE_NOTIFY_SET | REDISMODULE_NOTIFY_HASH | REDISMODULE_NOTIFY_ZSET | REDISMODULE_NOTIFY_EXPIRED | REDISMODULE_NOTIFY_EVICTED | REDISMODULE_NOTIFY_STREAM | REDISMODULE_NOTIFY_MODULE) /* A */ 196 | 197 | /* A special pointer that we can use between the core and the module to signal 198 | * field deletion, and that is impossible to be a valid pointer. */ 199 | #define REDISMODULE_HASH_DELETE ((RedisModuleString*)(long)1) 200 | 201 | /* Error messages. */ 202 | #define REDISMODULE_ERRORMSG_WRONGTYPE "WRONGTYPE Operation against a key holding the wrong kind of value" 203 | 204 | #define REDISMODULE_POSITIVE_INFINITE (1.0/0.0) 205 | #define REDISMODULE_NEGATIVE_INFINITE (-1.0/0.0) 206 | 207 | /* Cluster API defines. */ 208 | #define REDISMODULE_NODE_ID_LEN 40 209 | #define REDISMODULE_NODE_MYSELF (1<<0) 210 | #define REDISMODULE_NODE_MASTER (1<<1) 211 | #define REDISMODULE_NODE_SLAVE (1<<2) 212 | #define REDISMODULE_NODE_PFAIL (1<<3) 213 | #define REDISMODULE_NODE_FAIL (1<<4) 214 | #define REDISMODULE_NODE_NOFAILOVER (1<<5) 215 | 216 | #define REDISMODULE_CLUSTER_FLAG_NONE 0 217 | #define REDISMODULE_CLUSTER_FLAG_NO_FAILOVER (1<<1) 218 | #define REDISMODULE_CLUSTER_FLAG_NO_REDIRECTION (1<<2) 219 | 220 | #define REDISMODULE_NOT_USED(V) ((void) V) 221 | 222 | /* Logging level strings */ 223 | #define REDISMODULE_LOGLEVEL_DEBUG "debug" 224 | #define REDISMODULE_LOGLEVEL_VERBOSE "verbose" 225 | #define REDISMODULE_LOGLEVEL_NOTICE "notice" 226 | #define REDISMODULE_LOGLEVEL_WARNING "warning" 227 | 228 | /* Bit flags for aux_save_triggers and the aux_load and aux_save callbacks */ 229 | #define REDISMODULE_AUX_BEFORE_RDB (1<<0) 230 | #define REDISMODULE_AUX_AFTER_RDB (1<<1) 231 | 232 | /* RM_Yield flags */ 233 | #define REDISMODULE_YIELD_FLAG_NONE (1<<0) 234 | #define REDISMODULE_YIELD_FLAG_CLIENTS (1<<1) 235 | 236 | /* This type represents a timer handle, and is returned when a timer is 237 | * registered and used in order to invalidate a timer. It's just a 64 bit 238 | * number, because this is how each timer is represented inside the radix tree 239 | * of timers that are going to expire, sorted by expire time. */ 240 | typedef uint64_t RedisModuleTimerID; 241 | 242 | /* CommandFilter Flags */ 243 | 244 | /* Do filter RedisModule_Call() commands initiated by module itself. */ 245 | #define REDISMODULE_CMDFILTER_NOSELF (1<<0) 246 | 247 | /* Declare that the module can handle errors with RedisModule_SetModuleOptions. */ 248 | #define REDISMODULE_OPTIONS_HANDLE_IO_ERRORS (1<<0) 249 | 250 | /* When set, Redis will not call RedisModule_SignalModifiedKey(), implicitly in 251 | * RedisModule_CloseKey, and the module needs to do that when manually when keys 252 | * are modified from the user's perspective, to invalidate WATCH. */ 253 | #define REDISMODULE_OPTION_NO_IMPLICIT_SIGNAL_MODIFIED (1<<1) 254 | 255 | /* Declare that the module can handle diskless async replication with RedisModule_SetModuleOptions. */ 256 | #define REDISMODULE_OPTIONS_HANDLE_REPL_ASYNC_LOAD (1<<2) 257 | 258 | /* Definitions for RedisModule_SetCommandInfo. */ 259 | 260 | typedef enum { 261 | REDISMODULE_ARG_TYPE_STRING, 262 | REDISMODULE_ARG_TYPE_INTEGER, 263 | REDISMODULE_ARG_TYPE_DOUBLE, 264 | REDISMODULE_ARG_TYPE_KEY, /* A string, but represents a keyname */ 265 | REDISMODULE_ARG_TYPE_PATTERN, 266 | REDISMODULE_ARG_TYPE_UNIX_TIME, 267 | REDISMODULE_ARG_TYPE_PURE_TOKEN, 268 | REDISMODULE_ARG_TYPE_ONEOF, /* Must have sub-arguments */ 269 | REDISMODULE_ARG_TYPE_BLOCK /* Must have sub-arguments */ 270 | } RedisModuleCommandArgType; 271 | 272 | #define REDISMODULE_CMD_ARG_NONE (0) 273 | #define REDISMODULE_CMD_ARG_OPTIONAL (1<<0) /* The argument is optional (like GET in SET command) */ 274 | #define REDISMODULE_CMD_ARG_MULTIPLE (1<<1) /* The argument may repeat itself (like key in DEL) */ 275 | #define REDISMODULE_CMD_ARG_MULTIPLE_TOKEN (1<<2) /* The argument may repeat itself, and so does its token (like `GET pattern` in SORT) */ 276 | #define _REDISMODULE_CMD_ARG_NEXT (1<<3) 277 | 278 | typedef enum { 279 | REDISMODULE_KSPEC_BS_INVALID = 0, /* Must be zero. An implicitly value of 280 | * zero is provided when the field is 281 | * absent in a struct literal. */ 282 | REDISMODULE_KSPEC_BS_UNKNOWN, 283 | REDISMODULE_KSPEC_BS_INDEX, 284 | REDISMODULE_KSPEC_BS_KEYWORD 285 | } RedisModuleKeySpecBeginSearchType; 286 | 287 | typedef enum { 288 | REDISMODULE_KSPEC_FK_OMITTED = 0, /* Used when the field is absent in a 289 | * struct literal. Don't use this value 290 | * explicitly. */ 291 | REDISMODULE_KSPEC_FK_UNKNOWN, 292 | REDISMODULE_KSPEC_FK_RANGE, 293 | REDISMODULE_KSPEC_FK_KEYNUM 294 | } RedisModuleKeySpecFindKeysType; 295 | 296 | /* Key-spec flags. For details, see the documentation of 297 | * RedisModule_SetCommandInfo and the key-spec flags in server.h. */ 298 | #define REDISMODULE_CMD_KEY_RO (1ULL<<0) 299 | #define REDISMODULE_CMD_KEY_RW (1ULL<<1) 300 | #define REDISMODULE_CMD_KEY_OW (1ULL<<2) 301 | #define REDISMODULE_CMD_KEY_RM (1ULL<<3) 302 | #define REDISMODULE_CMD_KEY_ACCESS (1ULL<<4) 303 | #define REDISMODULE_CMD_KEY_UPDATE (1ULL<<5) 304 | #define REDISMODULE_CMD_KEY_INSERT (1ULL<<6) 305 | #define REDISMODULE_CMD_KEY_DELETE (1ULL<<7) 306 | #define REDISMODULE_CMD_KEY_NOT_KEY (1ULL<<8) 307 | #define REDISMODULE_CMD_KEY_INCOMPLETE (1ULL<<9) 308 | #define REDISMODULE_CMD_KEY_VARIABLE_FLAGS (1ULL<<10) 309 | 310 | /* Channel flags, for details see the documentation of 311 | * RedisModule_ChannelAtPosWithFlags. */ 312 | #define REDISMODULE_CMD_CHANNEL_PATTERN (1ULL<<0) 313 | #define REDISMODULE_CMD_CHANNEL_PUBLISH (1ULL<<1) 314 | #define REDISMODULE_CMD_CHANNEL_SUBSCRIBE (1ULL<<2) 315 | #define REDISMODULE_CMD_CHANNEL_UNSUBSCRIBE (1ULL<<3) 316 | 317 | typedef struct RedisModuleCommandArg { 318 | const char *name; 319 | RedisModuleCommandArgType type; 320 | int key_spec_index; /* If type is KEY, this is a zero-based index of 321 | * the key_spec in the command. For other types, 322 | * you may specify -1. */ 323 | const char *token; /* If type is PURE_TOKEN, this is the token. */ 324 | const char *summary; 325 | const char *since; 326 | int flags; /* The REDISMODULE_CMD_ARG_* macros. */ 327 | const char *deprecated_since; 328 | struct RedisModuleCommandArg *subargs; 329 | } RedisModuleCommandArg; 330 | 331 | typedef struct { 332 | const char *since; 333 | const char *changes; 334 | } RedisModuleCommandHistoryEntry; 335 | 336 | typedef struct { 337 | const char *notes; 338 | uint64_t flags; /* REDISMODULE_CMD_KEY_* macros. */ 339 | RedisModuleKeySpecBeginSearchType begin_search_type; 340 | union { 341 | struct { 342 | /* The index from which we start the search for keys */ 343 | int pos; 344 | } index; 345 | struct { 346 | /* The keyword that indicates the beginning of key args */ 347 | const char *keyword; 348 | /* An index in argv from which to start searching. 349 | * Can be negative, which means start search from the end, in reverse 350 | * (Example: -2 means to start in reverse from the penultimate arg) */ 351 | int startfrom; 352 | } keyword; 353 | } bs; 354 | RedisModuleKeySpecFindKeysType find_keys_type; 355 | union { 356 | struct { 357 | /* Index of the last key relative to the result of the begin search 358 | * step. Can be negative, in which case it's not relative. -1 359 | * indicating till the last argument, -2 one before the last and so 360 | * on. */ 361 | int lastkey; 362 | /* How many args should we skip after finding a key, in order to 363 | * find the next one. */ 364 | int keystep; 365 | /* If lastkey is -1, we use limit to stop the search by a factor. 0 366 | * and 1 mean no limit. 2 means 1/2 of the remaining args, 3 means 367 | * 1/3, and so on. */ 368 | int limit; 369 | } range; 370 | struct { 371 | /* Index of the argument containing the number of keys to come 372 | * relative to the result of the begin search step */ 373 | int keynumidx; 374 | /* Index of the fist key. (Usually it's just after keynumidx, in 375 | * which case it should be set to keynumidx + 1.) */ 376 | int firstkey; 377 | /* How many args should we skip after finding a key, in order to 378 | * find the next one, relative to the result of the begin search 379 | * step. */ 380 | int keystep; 381 | } keynum; 382 | } fk; 383 | } RedisModuleCommandKeySpec; 384 | 385 | typedef struct { 386 | int version; 387 | size_t sizeof_historyentry; 388 | size_t sizeof_keyspec; 389 | size_t sizeof_arg; 390 | } RedisModuleCommandInfoVersion; 391 | 392 | static const RedisModuleCommandInfoVersion RedisModule_CurrentCommandInfoVersion = { 393 | .version = 1, 394 | .sizeof_historyentry = sizeof(RedisModuleCommandHistoryEntry), 395 | .sizeof_keyspec = sizeof(RedisModuleCommandKeySpec), 396 | .sizeof_arg = sizeof(RedisModuleCommandArg) 397 | }; 398 | 399 | #define REDISMODULE_COMMAND_INFO_VERSION (&RedisModule_CurrentCommandInfoVersion) 400 | 401 | typedef struct { 402 | /* Always set version to REDISMODULE_COMMAND_INFO_VERSION */ 403 | const RedisModuleCommandInfoVersion *version; 404 | /* Version 1 fields (added in Redis 7.0.0) */ 405 | const char *summary; /* Summary of the command */ 406 | const char *complexity; /* Complexity description */ 407 | const char *since; /* Debut module version of the command */ 408 | RedisModuleCommandHistoryEntry *history; /* History */ 409 | /* A string of space-separated tips meant for clients/proxies regarding this 410 | * command */ 411 | const char *tips; 412 | /* Number of arguments, it is possible to use -N to say >= N */ 413 | int arity; 414 | RedisModuleCommandKeySpec *key_specs; 415 | RedisModuleCommandArg *args; 416 | } RedisModuleCommandInfo; 417 | 418 | /* Eventloop definitions. */ 419 | #define REDISMODULE_EVENTLOOP_READABLE 1 420 | #define REDISMODULE_EVENTLOOP_WRITABLE 2 421 | typedef void (*RedisModuleEventLoopFunc)(int fd, void *user_data, int mask); 422 | typedef void (*RedisModuleEventLoopOneShotFunc)(void *user_data); 423 | 424 | /* Server events definitions. 425 | * Those flags should not be used directly by the module, instead 426 | * the module should use RedisModuleEvent_* variables. 427 | * Note: This must be synced with moduleEventVersions */ 428 | #define REDISMODULE_EVENT_REPLICATION_ROLE_CHANGED 0 429 | #define REDISMODULE_EVENT_PERSISTENCE 1 430 | #define REDISMODULE_EVENT_FLUSHDB 2 431 | #define REDISMODULE_EVENT_LOADING 3 432 | #define REDISMODULE_EVENT_CLIENT_CHANGE 4 433 | #define REDISMODULE_EVENT_SHUTDOWN 5 434 | #define REDISMODULE_EVENT_REPLICA_CHANGE 6 435 | #define REDISMODULE_EVENT_MASTER_LINK_CHANGE 7 436 | #define REDISMODULE_EVENT_CRON_LOOP 8 437 | #define REDISMODULE_EVENT_MODULE_CHANGE 9 438 | #define REDISMODULE_EVENT_LOADING_PROGRESS 10 439 | #define REDISMODULE_EVENT_SWAPDB 11 440 | #define REDISMODULE_EVENT_REPL_BACKUP 12 /* Deprecated since Redis 7.0, not used anymore. */ 441 | #define REDISMODULE_EVENT_FORK_CHILD 13 442 | #define REDISMODULE_EVENT_REPL_ASYNC_LOAD 14 443 | #define REDISMODULE_EVENT_EVENTLOOP 15 444 | #define REDISMODULE_EVENT_CONFIG 16 445 | #define _REDISMODULE_EVENT_NEXT 17 /* Next event flag, should be updated if a new event added. */ 446 | 447 | typedef struct RedisModuleEvent { 448 | uint64_t id; /* REDISMODULE_EVENT_... defines. */ 449 | uint64_t dataver; /* Version of the structure we pass as 'data'. */ 450 | } RedisModuleEvent; 451 | 452 | struct RedisModuleCtx; 453 | struct RedisModuleDefragCtx; 454 | typedef void (*RedisModuleEventCallback)(struct RedisModuleCtx *ctx, RedisModuleEvent eid, uint64_t subevent, void *data); 455 | 456 | /* IMPORTANT: When adding a new version of one of below structures that contain 457 | * event data (RedisModuleFlushInfoV1 for example) we have to avoid renaming the 458 | * old RedisModuleEvent structure. 459 | * For example, if we want to add RedisModuleFlushInfoV2, the RedisModuleEvent 460 | * structures should be: 461 | * RedisModuleEvent_FlushDB = { 462 | * REDISMODULE_EVENT_FLUSHDB, 463 | * 1 464 | * }, 465 | * RedisModuleEvent_FlushDBV2 = { 466 | * REDISMODULE_EVENT_FLUSHDB, 467 | * 2 468 | * } 469 | * and NOT: 470 | * RedisModuleEvent_FlushDBV1 = { 471 | * REDISMODULE_EVENT_FLUSHDB, 472 | * 1 473 | * }, 474 | * RedisModuleEvent_FlushDB = { 475 | * REDISMODULE_EVENT_FLUSHDB, 476 | * 2 477 | * } 478 | * The reason for that is forward-compatibility: We want that module that 479 | * compiled with a new redismodule.h to be able to work with a old server, 480 | * unless the author explicitly decided to use the newer event type. 481 | */ 482 | static const RedisModuleEvent 483 | RedisModuleEvent_ReplicationRoleChanged = { 484 | REDISMODULE_EVENT_REPLICATION_ROLE_CHANGED, 485 | 1 486 | }, 487 | RedisModuleEvent_Persistence = { 488 | REDISMODULE_EVENT_PERSISTENCE, 489 | 1 490 | }, 491 | RedisModuleEvent_FlushDB = { 492 | REDISMODULE_EVENT_FLUSHDB, 493 | 1 494 | }, 495 | RedisModuleEvent_Loading = { 496 | REDISMODULE_EVENT_LOADING, 497 | 1 498 | }, 499 | RedisModuleEvent_ClientChange = { 500 | REDISMODULE_EVENT_CLIENT_CHANGE, 501 | 1 502 | }, 503 | RedisModuleEvent_Shutdown = { 504 | REDISMODULE_EVENT_SHUTDOWN, 505 | 1 506 | }, 507 | RedisModuleEvent_ReplicaChange = { 508 | REDISMODULE_EVENT_REPLICA_CHANGE, 509 | 1 510 | }, 511 | RedisModuleEvent_CronLoop = { 512 | REDISMODULE_EVENT_CRON_LOOP, 513 | 1 514 | }, 515 | RedisModuleEvent_MasterLinkChange = { 516 | REDISMODULE_EVENT_MASTER_LINK_CHANGE, 517 | 1 518 | }, 519 | RedisModuleEvent_ModuleChange = { 520 | REDISMODULE_EVENT_MODULE_CHANGE, 521 | 1 522 | }, 523 | RedisModuleEvent_LoadingProgress = { 524 | REDISMODULE_EVENT_LOADING_PROGRESS, 525 | 1 526 | }, 527 | RedisModuleEvent_SwapDB = { 528 | REDISMODULE_EVENT_SWAPDB, 529 | 1 530 | }, 531 | /* Deprecated since Redis 7.0, not used anymore. */ 532 | __attribute__ ((deprecated)) 533 | RedisModuleEvent_ReplBackup = { 534 | REDISMODULE_EVENT_REPL_BACKUP, 535 | 1 536 | }, 537 | RedisModuleEvent_ReplAsyncLoad = { 538 | REDISMODULE_EVENT_REPL_ASYNC_LOAD, 539 | 1 540 | }, 541 | RedisModuleEvent_ForkChild = { 542 | REDISMODULE_EVENT_FORK_CHILD, 543 | 1 544 | }, 545 | RedisModuleEvent_EventLoop = { 546 | REDISMODULE_EVENT_EVENTLOOP, 547 | 1 548 | }, 549 | RedisModuleEvent_Config = { 550 | REDISMODULE_EVENT_CONFIG, 551 | 1 552 | }; 553 | 554 | /* Those are values that are used for the 'subevent' callback argument. */ 555 | #define REDISMODULE_SUBEVENT_PERSISTENCE_RDB_START 0 556 | #define REDISMODULE_SUBEVENT_PERSISTENCE_AOF_START 1 557 | #define REDISMODULE_SUBEVENT_PERSISTENCE_SYNC_RDB_START 2 558 | #define REDISMODULE_SUBEVENT_PERSISTENCE_ENDED 3 559 | #define REDISMODULE_SUBEVENT_PERSISTENCE_FAILED 4 560 | #define REDISMODULE_SUBEVENT_PERSISTENCE_SYNC_AOF_START 5 561 | #define _REDISMODULE_SUBEVENT_PERSISTENCE_NEXT 6 562 | 563 | #define REDISMODULE_SUBEVENT_LOADING_RDB_START 0 564 | #define REDISMODULE_SUBEVENT_LOADING_AOF_START 1 565 | #define REDISMODULE_SUBEVENT_LOADING_REPL_START 2 566 | #define REDISMODULE_SUBEVENT_LOADING_ENDED 3 567 | #define REDISMODULE_SUBEVENT_LOADING_FAILED 4 568 | #define _REDISMODULE_SUBEVENT_LOADING_NEXT 5 569 | 570 | #define REDISMODULE_SUBEVENT_CLIENT_CHANGE_CONNECTED 0 571 | #define REDISMODULE_SUBEVENT_CLIENT_CHANGE_DISCONNECTED 1 572 | #define _REDISMODULE_SUBEVENT_CLIENT_CHANGE_NEXT 2 573 | 574 | #define REDISMODULE_SUBEVENT_MASTER_LINK_UP 0 575 | #define REDISMODULE_SUBEVENT_MASTER_LINK_DOWN 1 576 | #define _REDISMODULE_SUBEVENT_MASTER_NEXT 2 577 | 578 | #define REDISMODULE_SUBEVENT_REPLICA_CHANGE_ONLINE 0 579 | #define REDISMODULE_SUBEVENT_REPLICA_CHANGE_OFFLINE 1 580 | #define _REDISMODULE_SUBEVENT_REPLICA_CHANGE_NEXT 2 581 | 582 | #define REDISMODULE_EVENT_REPLROLECHANGED_NOW_MASTER 0 583 | #define REDISMODULE_EVENT_REPLROLECHANGED_NOW_REPLICA 1 584 | #define _REDISMODULE_EVENT_REPLROLECHANGED_NEXT 2 585 | 586 | #define REDISMODULE_SUBEVENT_FLUSHDB_START 0 587 | #define REDISMODULE_SUBEVENT_FLUSHDB_END 1 588 | #define _REDISMODULE_SUBEVENT_FLUSHDB_NEXT 2 589 | 590 | #define REDISMODULE_SUBEVENT_MODULE_LOADED 0 591 | #define REDISMODULE_SUBEVENT_MODULE_UNLOADED 1 592 | #define _REDISMODULE_SUBEVENT_MODULE_NEXT 2 593 | 594 | #define REDISMODULE_SUBEVENT_CONFIG_CHANGE 0 595 | #define _REDISMODULE_SUBEVENT_CONFIG_NEXT 1 596 | 597 | #define REDISMODULE_SUBEVENT_LOADING_PROGRESS_RDB 0 598 | #define REDISMODULE_SUBEVENT_LOADING_PROGRESS_AOF 1 599 | #define _REDISMODULE_SUBEVENT_LOADING_PROGRESS_NEXT 2 600 | 601 | /* Replication Backup events are deprecated since Redis 7.0 and are never fired. */ 602 | #define REDISMODULE_SUBEVENT_REPL_BACKUP_CREATE 0 603 | #define REDISMODULE_SUBEVENT_REPL_BACKUP_RESTORE 1 604 | #define REDISMODULE_SUBEVENT_REPL_BACKUP_DISCARD 2 605 | #define _REDISMODULE_SUBEVENT_REPL_BACKUP_NEXT 3 606 | 607 | #define REDISMODULE_SUBEVENT_REPL_ASYNC_LOAD_STARTED 0 608 | #define REDISMODULE_SUBEVENT_REPL_ASYNC_LOAD_ABORTED 1 609 | #define REDISMODULE_SUBEVENT_REPL_ASYNC_LOAD_COMPLETED 2 610 | #define _REDISMODULE_SUBEVENT_REPL_ASYNC_LOAD_NEXT 3 611 | 612 | #define REDISMODULE_SUBEVENT_FORK_CHILD_BORN 0 613 | #define REDISMODULE_SUBEVENT_FORK_CHILD_DIED 1 614 | #define _REDISMODULE_SUBEVENT_FORK_CHILD_NEXT 2 615 | 616 | #define REDISMODULE_SUBEVENT_EVENTLOOP_BEFORE_SLEEP 0 617 | #define REDISMODULE_SUBEVENT_EVENTLOOP_AFTER_SLEEP 1 618 | #define _REDISMODULE_SUBEVENT_EVENTLOOP_NEXT 2 619 | 620 | #define _REDISMODULE_SUBEVENT_SHUTDOWN_NEXT 0 621 | #define _REDISMODULE_SUBEVENT_CRON_LOOP_NEXT 0 622 | #define _REDISMODULE_SUBEVENT_SWAPDB_NEXT 0 623 | 624 | /* RedisModuleClientInfo flags. */ 625 | #define REDISMODULE_CLIENTINFO_FLAG_SSL (1<<0) 626 | #define REDISMODULE_CLIENTINFO_FLAG_PUBSUB (1<<1) 627 | #define REDISMODULE_CLIENTINFO_FLAG_BLOCKED (1<<2) 628 | #define REDISMODULE_CLIENTINFO_FLAG_TRACKING (1<<3) 629 | #define REDISMODULE_CLIENTINFO_FLAG_UNIXSOCKET (1<<4) 630 | #define REDISMODULE_CLIENTINFO_FLAG_MULTI (1<<5) 631 | 632 | /* Here we take all the structures that the module pass to the core 633 | * and the other way around. Notably the list here contains the structures 634 | * used by the hooks API RedisModule_RegisterToServerEvent(). 635 | * 636 | * The structures always start with a 'version' field. This is useful 637 | * when we want to pass a reference to the structure to the core APIs, 638 | * for the APIs to fill the structure. In that case, the structure 'version' 639 | * field is initialized before passing it to the core, so that the core is 640 | * able to cast the pointer to the appropriate structure version. In this 641 | * way we obtain ABI compatibility. 642 | * 643 | * Here we'll list all the structure versions in case they evolve over time, 644 | * however using a define, we'll make sure to use the last version as the 645 | * public name for the module to use. */ 646 | 647 | #define REDISMODULE_CLIENTINFO_VERSION 1 648 | typedef struct RedisModuleClientInfo { 649 | uint64_t version; /* Version of this structure for ABI compat. */ 650 | uint64_t flags; /* REDISMODULE_CLIENTINFO_FLAG_* */ 651 | uint64_t id; /* Client ID. */ 652 | char addr[46]; /* IPv4 or IPv6 address. */ 653 | uint16_t port; /* TCP port. */ 654 | uint16_t db; /* Selected DB. */ 655 | } RedisModuleClientInfoV1; 656 | 657 | #define RedisModuleClientInfo RedisModuleClientInfoV1 658 | 659 | #define REDISMODULE_CLIENTINFO_INITIALIZER_V1 { .version = 1 } 660 | 661 | #define REDISMODULE_REPLICATIONINFO_VERSION 1 662 | typedef struct RedisModuleReplicationInfo { 663 | uint64_t version; /* Not used since this structure is never passed 664 | from the module to the core right now. Here 665 | for future compatibility. */ 666 | int master; /* true if master, false if replica */ 667 | char *masterhost; /* master instance hostname for NOW_REPLICA */ 668 | int masterport; /* master instance port for NOW_REPLICA */ 669 | char *replid1; /* Main replication ID */ 670 | char *replid2; /* Secondary replication ID */ 671 | uint64_t repl1_offset; /* Main replication offset */ 672 | uint64_t repl2_offset; /* Offset of replid2 validity */ 673 | } RedisModuleReplicationInfoV1; 674 | 675 | #define RedisModuleReplicationInfo RedisModuleReplicationInfoV1 676 | 677 | #define REDISMODULE_FLUSHINFO_VERSION 1 678 | typedef struct RedisModuleFlushInfo { 679 | uint64_t version; /* Not used since this structure is never passed 680 | from the module to the core right now. Here 681 | for future compatibility. */ 682 | int32_t sync; /* Synchronous or threaded flush?. */ 683 | int32_t dbnum; /* Flushed database number, -1 for ALL. */ 684 | } RedisModuleFlushInfoV1; 685 | 686 | #define RedisModuleFlushInfo RedisModuleFlushInfoV1 687 | 688 | #define REDISMODULE_MODULE_CHANGE_VERSION 1 689 | typedef struct RedisModuleModuleChange { 690 | uint64_t version; /* Not used since this structure is never passed 691 | from the module to the core right now. Here 692 | for future compatibility. */ 693 | const char* module_name;/* Name of module loaded or unloaded. */ 694 | int32_t module_version; /* Module version. */ 695 | } RedisModuleModuleChangeV1; 696 | 697 | #define RedisModuleModuleChange RedisModuleModuleChangeV1 698 | 699 | #define REDISMODULE_CONFIGCHANGE_VERSION 1 700 | typedef struct RedisModuleConfigChange { 701 | uint64_t version; /* Not used since this structure is never passed 702 | from the module to the core right now. Here 703 | for future compatibility. */ 704 | uint32_t num_changes; /* how many redis config options were changed */ 705 | const char **config_names; /* the config names that were changed */ 706 | } RedisModuleConfigChangeV1; 707 | 708 | #define RedisModuleConfigChange RedisModuleConfigChangeV1 709 | 710 | #define REDISMODULE_CRON_LOOP_VERSION 1 711 | typedef struct RedisModuleCronLoopInfo { 712 | uint64_t version; /* Not used since this structure is never passed 713 | from the module to the core right now. Here 714 | for future compatibility. */ 715 | int32_t hz; /* Approximate number of events per second. */ 716 | } RedisModuleCronLoopV1; 717 | 718 | #define RedisModuleCronLoop RedisModuleCronLoopV1 719 | 720 | #define REDISMODULE_LOADING_PROGRESS_VERSION 1 721 | typedef struct RedisModuleLoadingProgressInfo { 722 | uint64_t version; /* Not used since this structure is never passed 723 | from the module to the core right now. Here 724 | for future compatibility. */ 725 | int32_t hz; /* Approximate number of events per second. */ 726 | int32_t progress; /* Approximate progress between 0 and 1024, or -1 727 | * if unknown. */ 728 | } RedisModuleLoadingProgressV1; 729 | 730 | #define RedisModuleLoadingProgress RedisModuleLoadingProgressV1 731 | 732 | #define REDISMODULE_SWAPDBINFO_VERSION 1 733 | typedef struct RedisModuleSwapDbInfo { 734 | uint64_t version; /* Not used since this structure is never passed 735 | from the module to the core right now. Here 736 | for future compatibility. */ 737 | int32_t dbnum_first; /* Swap Db first dbnum */ 738 | int32_t dbnum_second; /* Swap Db second dbnum */ 739 | } RedisModuleSwapDbInfoV1; 740 | 741 | #define RedisModuleSwapDbInfo RedisModuleSwapDbInfoV1 742 | 743 | typedef enum { 744 | REDISMODULE_ACL_LOG_AUTH = 0, /* Authentication failure */ 745 | REDISMODULE_ACL_LOG_CMD, /* Command authorization failure */ 746 | REDISMODULE_ACL_LOG_KEY, /* Key authorization failure */ 747 | REDISMODULE_ACL_LOG_CHANNEL /* Channel authorization failure */ 748 | } RedisModuleACLLogEntryReason; 749 | 750 | /* ------------------------- End of common defines ------------------------ */ 751 | 752 | #ifndef REDISMODULE_CORE 753 | 754 | typedef long long mstime_t; 755 | 756 | /* Macro definitions specific to individual compilers */ 757 | #ifndef REDISMODULE_ATTR_UNUSED 758 | # ifdef __GNUC__ 759 | # define REDISMODULE_ATTR_UNUSED __attribute__((unused)) 760 | # else 761 | # define REDISMODULE_ATTR_UNUSED 762 | # endif 763 | #endif 764 | 765 | #ifndef REDISMODULE_ATTR_PRINTF 766 | # ifdef __GNUC__ 767 | # define REDISMODULE_ATTR_PRINTF(idx,cnt) __attribute__((format(printf,idx,cnt))) 768 | # else 769 | # define REDISMODULE_ATTR_PRINTF(idx,cnt) 770 | # endif 771 | #endif 772 | 773 | #ifndef REDISMODULE_ATTR_COMMON 774 | # if defined(__GNUC__) && !(defined(__clang__) && defined(__cplusplus)) 775 | # define REDISMODULE_ATTR_COMMON __attribute__((__common__)) 776 | # else 777 | # define REDISMODULE_ATTR_COMMON 778 | # endif 779 | #endif 780 | 781 | /* Incomplete structures for compiler checks but opaque access. */ 782 | typedef struct RedisModuleCtx RedisModuleCtx; 783 | typedef struct RedisModuleCommand RedisModuleCommand; 784 | typedef struct RedisModuleKey RedisModuleKey; 785 | typedef struct RedisModuleString RedisModuleString; 786 | typedef struct RedisModuleCallReply RedisModuleCallReply; 787 | typedef struct RedisModuleIO RedisModuleIO; 788 | typedef struct RedisModuleType RedisModuleType; 789 | typedef struct RedisModuleDigest RedisModuleDigest; 790 | typedef struct RedisModuleBlockedClient RedisModuleBlockedClient; 791 | typedef struct RedisModuleClusterInfo RedisModuleClusterInfo; 792 | typedef struct RedisModuleDict RedisModuleDict; 793 | typedef struct RedisModuleDictIter RedisModuleDictIter; 794 | typedef struct RedisModuleCommandFilterCtx RedisModuleCommandFilterCtx; 795 | typedef struct RedisModuleCommandFilter RedisModuleCommandFilter; 796 | typedef struct RedisModuleInfoCtx RedisModuleInfoCtx; 797 | typedef struct RedisModuleServerInfoData RedisModuleServerInfoData; 798 | typedef struct RedisModuleScanCursor RedisModuleScanCursor; 799 | typedef struct RedisModuleDefragCtx RedisModuleDefragCtx; 800 | typedef struct RedisModuleUser RedisModuleUser; 801 | typedef struct RedisModuleKeyOptCtx RedisModuleKeyOptCtx; 802 | 803 | typedef int (*RedisModuleCmdFunc)(RedisModuleCtx *ctx, RedisModuleString **argv, int argc); 804 | typedef void (*RedisModuleDisconnectFunc)(RedisModuleCtx *ctx, RedisModuleBlockedClient *bc); 805 | typedef int (*RedisModuleNotificationFunc)(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key); 806 | typedef void *(*RedisModuleTypeLoadFunc)(RedisModuleIO *rdb, int encver); 807 | typedef void (*RedisModuleTypeSaveFunc)(RedisModuleIO *rdb, void *value); 808 | typedef int (*RedisModuleTypeAuxLoadFunc)(RedisModuleIO *rdb, int encver, int when); 809 | typedef void (*RedisModuleTypeAuxSaveFunc)(RedisModuleIO *rdb, int when); 810 | typedef void (*RedisModuleTypeRewriteFunc)(RedisModuleIO *aof, RedisModuleString *key, void *value); 811 | typedef size_t (*RedisModuleTypeMemUsageFunc)(const void *value); 812 | typedef size_t (*RedisModuleTypeMemUsageFunc2)(RedisModuleKeyOptCtx *ctx, const void *value, size_t sample_size); 813 | typedef void (*RedisModuleTypeDigestFunc)(RedisModuleDigest *digest, void *value); 814 | typedef void (*RedisModuleTypeFreeFunc)(void *value); 815 | typedef size_t (*RedisModuleTypeFreeEffortFunc)(RedisModuleString *key, const void *value); 816 | typedef size_t (*RedisModuleTypeFreeEffortFunc2)(RedisModuleKeyOptCtx *ctx, const void *value); 817 | typedef void (*RedisModuleTypeUnlinkFunc)(RedisModuleString *key, const void *value); 818 | typedef void (*RedisModuleTypeUnlinkFunc2)(RedisModuleKeyOptCtx *ctx, const void *value); 819 | typedef void *(*RedisModuleTypeCopyFunc)(RedisModuleString *fromkey, RedisModuleString *tokey, const void *value); 820 | typedef void *(*RedisModuleTypeCopyFunc2)(RedisModuleKeyOptCtx *ctx, const void *value); 821 | typedef int (*RedisModuleTypeDefragFunc)(RedisModuleDefragCtx *ctx, RedisModuleString *key, void **value); 822 | typedef void (*RedisModuleClusterMessageReceiver)(RedisModuleCtx *ctx, const char *sender_id, uint8_t type, const unsigned char *payload, uint32_t len); 823 | typedef void (*RedisModuleTimerProc)(RedisModuleCtx *ctx, void *data); 824 | typedef void (*RedisModuleCommandFilterFunc) (RedisModuleCommandFilterCtx *filter); 825 | typedef void (*RedisModuleForkDoneHandler) (int exitcode, int bysignal, void *user_data); 826 | typedef void (*RedisModuleInfoFunc)(RedisModuleInfoCtx *ctx, int for_crash_report); 827 | typedef void (*RedisModuleScanCB)(RedisModuleCtx *ctx, RedisModuleString *keyname, RedisModuleKey *key, void *privdata); 828 | typedef void (*RedisModuleScanKeyCB)(RedisModuleKey *key, RedisModuleString *field, RedisModuleString *value, void *privdata); 829 | typedef void (*RedisModuleUserChangedFunc) (uint64_t client_id, void *privdata); 830 | typedef int (*RedisModuleDefragFunc)(RedisModuleDefragCtx *ctx); 831 | typedef RedisModuleString * (*RedisModuleConfigGetStringFunc)(const char *name, void *privdata); 832 | typedef long long (*RedisModuleConfigGetNumericFunc)(const char *name, void *privdata); 833 | typedef int (*RedisModuleConfigGetBoolFunc)(const char *name, void *privdata); 834 | typedef int (*RedisModuleConfigGetEnumFunc)(const char *name, void *privdata); 835 | typedef int (*RedisModuleConfigSetStringFunc)(const char *name, RedisModuleString *val, void *privdata, RedisModuleString **err); 836 | typedef int (*RedisModuleConfigSetNumericFunc)(const char *name, long long val, void *privdata, RedisModuleString **err); 837 | typedef int (*RedisModuleConfigSetBoolFunc)(const char *name, int val, void *privdata, RedisModuleString **err); 838 | typedef int (*RedisModuleConfigSetEnumFunc)(const char *name, int val, void *privdata, RedisModuleString **err); 839 | typedef int (*RedisModuleConfigApplyFunc)(RedisModuleCtx *ctx, void *privdata, RedisModuleString **err); 840 | 841 | typedef struct RedisModuleTypeMethods { 842 | uint64_t version; 843 | RedisModuleTypeLoadFunc rdb_load; 844 | RedisModuleTypeSaveFunc rdb_save; 845 | RedisModuleTypeRewriteFunc aof_rewrite; 846 | RedisModuleTypeMemUsageFunc mem_usage; 847 | RedisModuleTypeDigestFunc digest; 848 | RedisModuleTypeFreeFunc free; 849 | RedisModuleTypeAuxLoadFunc aux_load; 850 | RedisModuleTypeAuxSaveFunc aux_save; 851 | int aux_save_triggers; 852 | RedisModuleTypeFreeEffortFunc free_effort; 853 | RedisModuleTypeUnlinkFunc unlink; 854 | RedisModuleTypeCopyFunc copy; 855 | RedisModuleTypeDefragFunc defrag; 856 | RedisModuleTypeMemUsageFunc2 mem_usage2; 857 | RedisModuleTypeFreeEffortFunc2 free_effort2; 858 | RedisModuleTypeUnlinkFunc2 unlink2; 859 | RedisModuleTypeCopyFunc2 copy2; 860 | } RedisModuleTypeMethods; 861 | 862 | #define REDISMODULE_GET_API(name) \ 863 | RedisModule_GetApi("RedisModule_" #name, ((void **)&RedisModule_ ## name)) 864 | 865 | /* Default API declaration prefix (not 'extern' for backwards compatibility) */ 866 | #ifndef REDISMODULE_API 867 | #define REDISMODULE_API 868 | #endif 869 | 870 | /* Default API declaration suffix (compiler attributes) */ 871 | #ifndef REDISMODULE_ATTR 872 | #define REDISMODULE_ATTR REDISMODULE_ATTR_COMMON 873 | #endif 874 | 875 | REDISMODULE_API void * (*RedisModule_Alloc)(size_t bytes) REDISMODULE_ATTR; 876 | REDISMODULE_API void * (*RedisModule_TryAlloc)(size_t bytes) REDISMODULE_ATTR; 877 | REDISMODULE_API void * (*RedisModule_Realloc)(void *ptr, size_t bytes) REDISMODULE_ATTR; 878 | REDISMODULE_API void (*RedisModule_Free)(void *ptr) REDISMODULE_ATTR; 879 | REDISMODULE_API void * (*RedisModule_Calloc)(size_t nmemb, size_t size) REDISMODULE_ATTR; 880 | REDISMODULE_API char * (*RedisModule_Strdup)(const char *str) REDISMODULE_ATTR; 881 | REDISMODULE_API int (*RedisModule_GetApi)(const char *, void *) REDISMODULE_ATTR; 882 | REDISMODULE_API int (*RedisModule_CreateCommand)(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep) REDISMODULE_ATTR; 883 | REDISMODULE_API RedisModuleCommand *(*RedisModule_GetCommand)(RedisModuleCtx *ctx, const char *name) REDISMODULE_ATTR; 884 | REDISMODULE_API int (*RedisModule_CreateSubcommand)(RedisModuleCommand *parent, const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep) REDISMODULE_ATTR; 885 | REDISMODULE_API int (*RedisModule_SetCommandInfo)(RedisModuleCommand *command, const RedisModuleCommandInfo *info) REDISMODULE_ATTR; 886 | REDISMODULE_API void (*RedisModule_SetModuleAttribs)(RedisModuleCtx *ctx, const char *name, int ver, int apiver) REDISMODULE_ATTR; 887 | REDISMODULE_API int (*RedisModule_IsModuleNameBusy)(const char *name) REDISMODULE_ATTR; 888 | REDISMODULE_API int (*RedisModule_WrongArity)(RedisModuleCtx *ctx) REDISMODULE_ATTR; 889 | REDISMODULE_API int (*RedisModule_ReplyWithLongLong)(RedisModuleCtx *ctx, long long ll) REDISMODULE_ATTR; 890 | REDISMODULE_API int (*RedisModule_GetSelectedDb)(RedisModuleCtx *ctx) REDISMODULE_ATTR; 891 | REDISMODULE_API int (*RedisModule_SelectDb)(RedisModuleCtx *ctx, int newid) REDISMODULE_ATTR; 892 | REDISMODULE_API int (*RedisModule_KeyExists)(RedisModuleCtx *ctx, RedisModuleString *keyname) REDISMODULE_ATTR; 893 | REDISMODULE_API RedisModuleKey * (*RedisModule_OpenKey)(RedisModuleCtx *ctx, RedisModuleString *keyname, int mode) REDISMODULE_ATTR; 894 | REDISMODULE_API void (*RedisModule_CloseKey)(RedisModuleKey *kp) REDISMODULE_ATTR; 895 | REDISMODULE_API int (*RedisModule_KeyType)(RedisModuleKey *kp) REDISMODULE_ATTR; 896 | REDISMODULE_API size_t (*RedisModule_ValueLength)(RedisModuleKey *kp) REDISMODULE_ATTR; 897 | REDISMODULE_API int (*RedisModule_ListPush)(RedisModuleKey *kp, int where, RedisModuleString *ele) REDISMODULE_ATTR; 898 | REDISMODULE_API RedisModuleString * (*RedisModule_ListPop)(RedisModuleKey *key, int where) REDISMODULE_ATTR; 899 | REDISMODULE_API RedisModuleString * (*RedisModule_ListGet)(RedisModuleKey *key, long index) REDISMODULE_ATTR; 900 | REDISMODULE_API int (*RedisModule_ListSet)(RedisModuleKey *key, long index, RedisModuleString *value) REDISMODULE_ATTR; 901 | REDISMODULE_API int (*RedisModule_ListInsert)(RedisModuleKey *key, long index, RedisModuleString *value) REDISMODULE_ATTR; 902 | REDISMODULE_API int (*RedisModule_ListDelete)(RedisModuleKey *key, long index) REDISMODULE_ATTR; 903 | REDISMODULE_API RedisModuleCallReply * (*RedisModule_Call)(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...) REDISMODULE_ATTR; 904 | REDISMODULE_API const char * (*RedisModule_CallReplyProto)(RedisModuleCallReply *reply, size_t *len) REDISMODULE_ATTR; 905 | REDISMODULE_API void (*RedisModule_FreeCallReply)(RedisModuleCallReply *reply) REDISMODULE_ATTR; 906 | REDISMODULE_API int (*RedisModule_CallReplyType)(RedisModuleCallReply *reply) REDISMODULE_ATTR; 907 | REDISMODULE_API long long (*RedisModule_CallReplyInteger)(RedisModuleCallReply *reply) REDISMODULE_ATTR; 908 | REDISMODULE_API double (*RedisModule_CallReplyDouble)(RedisModuleCallReply *reply) REDISMODULE_ATTR; 909 | REDISMODULE_API int (*RedisModule_CallReplyBool)(RedisModuleCallReply *reply) REDISMODULE_ATTR; 910 | REDISMODULE_API const char* (*RedisModule_CallReplyBigNumber)(RedisModuleCallReply *reply, size_t *len) REDISMODULE_ATTR; 911 | REDISMODULE_API const char* (*RedisModule_CallReplyVerbatim)(RedisModuleCallReply *reply, size_t *len, const char **format) REDISMODULE_ATTR; 912 | REDISMODULE_API RedisModuleCallReply * (*RedisModule_CallReplySetElement)(RedisModuleCallReply *reply, size_t idx) REDISMODULE_ATTR; 913 | REDISMODULE_API int (*RedisModule_CallReplyMapElement)(RedisModuleCallReply *reply, size_t idx, RedisModuleCallReply **key, RedisModuleCallReply **val) REDISMODULE_ATTR; 914 | REDISMODULE_API int (*RedisModule_CallReplyAttributeElement)(RedisModuleCallReply *reply, size_t idx, RedisModuleCallReply **key, RedisModuleCallReply **val) REDISMODULE_ATTR; 915 | REDISMODULE_API RedisModuleCallReply * (*RedisModule_CallReplyAttribute)(RedisModuleCallReply *reply) REDISMODULE_ATTR; 916 | REDISMODULE_API size_t (*RedisModule_CallReplyLength)(RedisModuleCallReply *reply) REDISMODULE_ATTR; 917 | REDISMODULE_API RedisModuleCallReply * (*RedisModule_CallReplyArrayElement)(RedisModuleCallReply *reply, size_t idx) REDISMODULE_ATTR; 918 | REDISMODULE_API RedisModuleString * (*RedisModule_CreateString)(RedisModuleCtx *ctx, const char *ptr, size_t len) REDISMODULE_ATTR; 919 | REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringFromLongLong)(RedisModuleCtx *ctx, long long ll) REDISMODULE_ATTR; 920 | REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringFromULongLong)(RedisModuleCtx *ctx, unsigned long long ull) REDISMODULE_ATTR; 921 | REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringFromDouble)(RedisModuleCtx *ctx, double d) REDISMODULE_ATTR; 922 | REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringFromLongDouble)(RedisModuleCtx *ctx, long double ld, int humanfriendly) REDISMODULE_ATTR; 923 | REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringFromString)(RedisModuleCtx *ctx, const RedisModuleString *str) REDISMODULE_ATTR; 924 | REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringFromStreamID)(RedisModuleCtx *ctx, const RedisModuleStreamID *id) REDISMODULE_ATTR; 925 | REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringPrintf)(RedisModuleCtx *ctx, const char *fmt, ...) REDISMODULE_ATTR_PRINTF(2,3) REDISMODULE_ATTR; 926 | REDISMODULE_API void (*RedisModule_FreeString)(RedisModuleCtx *ctx, RedisModuleString *str) REDISMODULE_ATTR; 927 | REDISMODULE_API const char * (*RedisModule_StringPtrLen)(const RedisModuleString *str, size_t *len) REDISMODULE_ATTR; 928 | REDISMODULE_API int (*RedisModule_ReplyWithError)(RedisModuleCtx *ctx, const char *err) REDISMODULE_ATTR; 929 | REDISMODULE_API int (*RedisModule_ReplyWithSimpleString)(RedisModuleCtx *ctx, const char *msg) REDISMODULE_ATTR; 930 | REDISMODULE_API int (*RedisModule_ReplyWithArray)(RedisModuleCtx *ctx, long len) REDISMODULE_ATTR; 931 | REDISMODULE_API int (*RedisModule_ReplyWithMap)(RedisModuleCtx *ctx, long len) REDISMODULE_ATTR; 932 | REDISMODULE_API int (*RedisModule_ReplyWithSet)(RedisModuleCtx *ctx, long len) REDISMODULE_ATTR; 933 | REDISMODULE_API int (*RedisModule_ReplyWithAttribute)(RedisModuleCtx *ctx, long len) REDISMODULE_ATTR; 934 | REDISMODULE_API int (*RedisModule_ReplyWithNullArray)(RedisModuleCtx *ctx) REDISMODULE_ATTR; 935 | REDISMODULE_API int (*RedisModule_ReplyWithEmptyArray)(RedisModuleCtx *ctx) REDISMODULE_ATTR; 936 | REDISMODULE_API void (*RedisModule_ReplySetArrayLength)(RedisModuleCtx *ctx, long len) REDISMODULE_ATTR; 937 | REDISMODULE_API void (*RedisModule_ReplySetMapLength)(RedisModuleCtx *ctx, long len) REDISMODULE_ATTR; 938 | REDISMODULE_API void (*RedisModule_ReplySetSetLength)(RedisModuleCtx *ctx, long len) REDISMODULE_ATTR; 939 | REDISMODULE_API void (*RedisModule_ReplySetAttributeLength)(RedisModuleCtx *ctx, long len) REDISMODULE_ATTR; 940 | REDISMODULE_API void (*RedisModule_ReplySetPushLength)(RedisModuleCtx *ctx, long len) REDISMODULE_ATTR; 941 | REDISMODULE_API int (*RedisModule_ReplyWithStringBuffer)(RedisModuleCtx *ctx, const char *buf, size_t len) REDISMODULE_ATTR; 942 | REDISMODULE_API int (*RedisModule_ReplyWithCString)(RedisModuleCtx *ctx, const char *buf) REDISMODULE_ATTR; 943 | REDISMODULE_API int (*RedisModule_ReplyWithString)(RedisModuleCtx *ctx, RedisModuleString *str) REDISMODULE_ATTR; 944 | REDISMODULE_API int (*RedisModule_ReplyWithEmptyString)(RedisModuleCtx *ctx) REDISMODULE_ATTR; 945 | REDISMODULE_API int (*RedisModule_ReplyWithVerbatimString)(RedisModuleCtx *ctx, const char *buf, size_t len) REDISMODULE_ATTR; 946 | REDISMODULE_API int (*RedisModule_ReplyWithVerbatimStringType)(RedisModuleCtx *ctx, const char *buf, size_t len, const char *ext) REDISMODULE_ATTR; 947 | REDISMODULE_API int (*RedisModule_ReplyWithNull)(RedisModuleCtx *ctx) REDISMODULE_ATTR; 948 | REDISMODULE_API int (*RedisModule_ReplyWithBool)(RedisModuleCtx *ctx, int b) REDISMODULE_ATTR; 949 | REDISMODULE_API int (*RedisModule_ReplyWithLongDouble)(RedisModuleCtx *ctx, long double d) REDISMODULE_ATTR; 950 | REDISMODULE_API int (*RedisModule_ReplyWithDouble)(RedisModuleCtx *ctx, double d) REDISMODULE_ATTR; 951 | REDISMODULE_API int (*RedisModule_ReplyWithBigNumber)(RedisModuleCtx *ctx, const char *bignum, size_t len) REDISMODULE_ATTR; 952 | REDISMODULE_API int (*RedisModule_ReplyWithCallReply)(RedisModuleCtx *ctx, RedisModuleCallReply *reply) REDISMODULE_ATTR; 953 | REDISMODULE_API int (*RedisModule_StringToLongLong)(const RedisModuleString *str, long long *ll) REDISMODULE_ATTR; 954 | REDISMODULE_API int (*RedisModule_StringToULongLong)(const RedisModuleString *str, unsigned long long *ull) REDISMODULE_ATTR; 955 | REDISMODULE_API int (*RedisModule_StringToDouble)(const RedisModuleString *str, double *d) REDISMODULE_ATTR; 956 | REDISMODULE_API int (*RedisModule_StringToLongDouble)(const RedisModuleString *str, long double *d) REDISMODULE_ATTR; 957 | REDISMODULE_API int (*RedisModule_StringToStreamID)(const RedisModuleString *str, RedisModuleStreamID *id) REDISMODULE_ATTR; 958 | REDISMODULE_API void (*RedisModule_AutoMemory)(RedisModuleCtx *ctx) REDISMODULE_ATTR; 959 | REDISMODULE_API int (*RedisModule_Replicate)(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...) REDISMODULE_ATTR; 960 | REDISMODULE_API int (*RedisModule_ReplicateVerbatim)(RedisModuleCtx *ctx) REDISMODULE_ATTR; 961 | REDISMODULE_API const char * (*RedisModule_CallReplyStringPtr)(RedisModuleCallReply *reply, size_t *len) REDISMODULE_ATTR; 962 | REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringFromCallReply)(RedisModuleCallReply *reply) REDISMODULE_ATTR; 963 | REDISMODULE_API int (*RedisModule_DeleteKey)(RedisModuleKey *key) REDISMODULE_ATTR; 964 | REDISMODULE_API int (*RedisModule_UnlinkKey)(RedisModuleKey *key) REDISMODULE_ATTR; 965 | REDISMODULE_API int (*RedisModule_StringSet)(RedisModuleKey *key, RedisModuleString *str) REDISMODULE_ATTR; 966 | REDISMODULE_API char * (*RedisModule_StringDMA)(RedisModuleKey *key, size_t *len, int mode) REDISMODULE_ATTR; 967 | REDISMODULE_API int (*RedisModule_StringTruncate)(RedisModuleKey *key, size_t newlen) REDISMODULE_ATTR; 968 | REDISMODULE_API mstime_t (*RedisModule_GetExpire)(RedisModuleKey *key) REDISMODULE_ATTR; 969 | REDISMODULE_API int (*RedisModule_SetExpire)(RedisModuleKey *key, mstime_t expire) REDISMODULE_ATTR; 970 | REDISMODULE_API mstime_t (*RedisModule_GetAbsExpire)(RedisModuleKey *key) REDISMODULE_ATTR; 971 | REDISMODULE_API int (*RedisModule_SetAbsExpire)(RedisModuleKey *key, mstime_t expire) REDISMODULE_ATTR; 972 | REDISMODULE_API void (*RedisModule_ResetDataset)(int restart_aof, int async) REDISMODULE_ATTR; 973 | REDISMODULE_API unsigned long long (*RedisModule_DbSize)(RedisModuleCtx *ctx) REDISMODULE_ATTR; 974 | REDISMODULE_API RedisModuleString * (*RedisModule_RandomKey)(RedisModuleCtx *ctx) REDISMODULE_ATTR; 975 | REDISMODULE_API int (*RedisModule_ZsetAdd)(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr) REDISMODULE_ATTR; 976 | REDISMODULE_API int (*RedisModule_ZsetIncrby)(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr, double *newscore) REDISMODULE_ATTR; 977 | REDISMODULE_API int (*RedisModule_ZsetScore)(RedisModuleKey *key, RedisModuleString *ele, double *score) REDISMODULE_ATTR; 978 | REDISMODULE_API int (*RedisModule_ZsetRem)(RedisModuleKey *key, RedisModuleString *ele, int *deleted) REDISMODULE_ATTR; 979 | REDISMODULE_API void (*RedisModule_ZsetRangeStop)(RedisModuleKey *key) REDISMODULE_ATTR; 980 | REDISMODULE_API int (*RedisModule_ZsetFirstInScoreRange)(RedisModuleKey *key, double min, double max, int minex, int maxex) REDISMODULE_ATTR; 981 | REDISMODULE_API int (*RedisModule_ZsetLastInScoreRange)(RedisModuleKey *key, double min, double max, int minex, int maxex) REDISMODULE_ATTR; 982 | REDISMODULE_API int (*RedisModule_ZsetFirstInLexRange)(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max) REDISMODULE_ATTR; 983 | REDISMODULE_API int (*RedisModule_ZsetLastInLexRange)(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max) REDISMODULE_ATTR; 984 | REDISMODULE_API RedisModuleString * (*RedisModule_ZsetRangeCurrentElement)(RedisModuleKey *key, double *score) REDISMODULE_ATTR; 985 | REDISMODULE_API int (*RedisModule_ZsetRangeNext)(RedisModuleKey *key) REDISMODULE_ATTR; 986 | REDISMODULE_API int (*RedisModule_ZsetRangePrev)(RedisModuleKey *key) REDISMODULE_ATTR; 987 | REDISMODULE_API int (*RedisModule_ZsetRangeEndReached)(RedisModuleKey *key) REDISMODULE_ATTR; 988 | REDISMODULE_API int (*RedisModule_HashSet)(RedisModuleKey *key, int flags, ...) REDISMODULE_ATTR; 989 | REDISMODULE_API int (*RedisModule_HashGet)(RedisModuleKey *key, int flags, ...) REDISMODULE_ATTR; 990 | REDISMODULE_API int (*RedisModule_StreamAdd)(RedisModuleKey *key, int flags, RedisModuleStreamID *id, RedisModuleString **argv, int64_t numfields) REDISMODULE_ATTR; 991 | REDISMODULE_API int (*RedisModule_StreamDelete)(RedisModuleKey *key, RedisModuleStreamID *id) REDISMODULE_ATTR; 992 | REDISMODULE_API int (*RedisModule_StreamIteratorStart)(RedisModuleKey *key, int flags, RedisModuleStreamID *startid, RedisModuleStreamID *endid) REDISMODULE_ATTR; 993 | REDISMODULE_API int (*RedisModule_StreamIteratorStop)(RedisModuleKey *key) REDISMODULE_ATTR; 994 | REDISMODULE_API int (*RedisModule_StreamIteratorNextID)(RedisModuleKey *key, RedisModuleStreamID *id, long *numfields) REDISMODULE_ATTR; 995 | REDISMODULE_API int (*RedisModule_StreamIteratorNextField)(RedisModuleKey *key, RedisModuleString **field_ptr, RedisModuleString **value_ptr) REDISMODULE_ATTR; 996 | REDISMODULE_API int (*RedisModule_StreamIteratorDelete)(RedisModuleKey *key) REDISMODULE_ATTR; 997 | REDISMODULE_API long long (*RedisModule_StreamTrimByLength)(RedisModuleKey *key, int flags, long long length) REDISMODULE_ATTR; 998 | REDISMODULE_API long long (*RedisModule_StreamTrimByID)(RedisModuleKey *key, int flags, RedisModuleStreamID *id) REDISMODULE_ATTR; 999 | REDISMODULE_API int (*RedisModule_IsKeysPositionRequest)(RedisModuleCtx *ctx) REDISMODULE_ATTR; 1000 | REDISMODULE_API void (*RedisModule_KeyAtPos)(RedisModuleCtx *ctx, int pos) REDISMODULE_ATTR; 1001 | REDISMODULE_API void (*RedisModule_KeyAtPosWithFlags)(RedisModuleCtx *ctx, int pos, int flags) REDISMODULE_ATTR; 1002 | REDISMODULE_API int (*RedisModule_IsChannelsPositionRequest)(RedisModuleCtx *ctx) REDISMODULE_ATTR; 1003 | REDISMODULE_API void (*RedisModule_ChannelAtPosWithFlags)(RedisModuleCtx *ctx, int pos, int flags) REDISMODULE_ATTR; 1004 | REDISMODULE_API unsigned long long (*RedisModule_GetClientId)(RedisModuleCtx *ctx) REDISMODULE_ATTR; 1005 | REDISMODULE_API RedisModuleString * (*RedisModule_GetClientUserNameById)(RedisModuleCtx *ctx, uint64_t id) REDISMODULE_ATTR; 1006 | REDISMODULE_API int (*RedisModule_GetClientInfoById)(void *ci, uint64_t id) REDISMODULE_ATTR; 1007 | REDISMODULE_API RedisModuleString * (*RedisModule_GetClientNameById)(RedisModuleCtx *ctx, uint64_t id) REDISMODULE_ATTR; 1008 | REDISMODULE_API int (*RedisModule_SetClientNameById)(uint64_t id, RedisModuleString *name) REDISMODULE_ATTR; 1009 | REDISMODULE_API int (*RedisModule_PublishMessage)(RedisModuleCtx *ctx, RedisModuleString *channel, RedisModuleString *message) REDISMODULE_ATTR; 1010 | REDISMODULE_API int (*RedisModule_PublishMessageShard)(RedisModuleCtx *ctx, RedisModuleString *channel, RedisModuleString *message) REDISMODULE_ATTR; 1011 | REDISMODULE_API int (*RedisModule_GetContextFlags)(RedisModuleCtx *ctx) REDISMODULE_ATTR; 1012 | REDISMODULE_API int (*RedisModule_AvoidReplicaTraffic)() REDISMODULE_ATTR; 1013 | REDISMODULE_API void * (*RedisModule_PoolAlloc)(RedisModuleCtx *ctx, size_t bytes) REDISMODULE_ATTR; 1014 | REDISMODULE_API RedisModuleType * (*RedisModule_CreateDataType)(RedisModuleCtx *ctx, const char *name, int encver, RedisModuleTypeMethods *typemethods) REDISMODULE_ATTR; 1015 | REDISMODULE_API int (*RedisModule_ModuleTypeSetValue)(RedisModuleKey *key, RedisModuleType *mt, void *value) REDISMODULE_ATTR; 1016 | REDISMODULE_API int (*RedisModule_ModuleTypeReplaceValue)(RedisModuleKey *key, RedisModuleType *mt, void *new_value, void **old_value) REDISMODULE_ATTR; 1017 | REDISMODULE_API RedisModuleType * (*RedisModule_ModuleTypeGetType)(RedisModuleKey *key) REDISMODULE_ATTR; 1018 | REDISMODULE_API void * (*RedisModule_ModuleTypeGetValue)(RedisModuleKey *key) REDISMODULE_ATTR; 1019 | REDISMODULE_API int (*RedisModule_IsIOError)(RedisModuleIO *io) REDISMODULE_ATTR; 1020 | REDISMODULE_API void (*RedisModule_SetModuleOptions)(RedisModuleCtx *ctx, int options) REDISMODULE_ATTR; 1021 | REDISMODULE_API int (*RedisModule_SignalModifiedKey)(RedisModuleCtx *ctx, RedisModuleString *keyname) REDISMODULE_ATTR; 1022 | REDISMODULE_API void (*RedisModule_SaveUnsigned)(RedisModuleIO *io, uint64_t value) REDISMODULE_ATTR; 1023 | REDISMODULE_API uint64_t (*RedisModule_LoadUnsigned)(RedisModuleIO *io) REDISMODULE_ATTR; 1024 | REDISMODULE_API void (*RedisModule_SaveSigned)(RedisModuleIO *io, int64_t value) REDISMODULE_ATTR; 1025 | REDISMODULE_API int64_t (*RedisModule_LoadSigned)(RedisModuleIO *io) REDISMODULE_ATTR; 1026 | REDISMODULE_API void (*RedisModule_EmitAOF)(RedisModuleIO *io, const char *cmdname, const char *fmt, ...) REDISMODULE_ATTR; 1027 | REDISMODULE_API void (*RedisModule_SaveString)(RedisModuleIO *io, RedisModuleString *s) REDISMODULE_ATTR; 1028 | REDISMODULE_API void (*RedisModule_SaveStringBuffer)(RedisModuleIO *io, const char *str, size_t len) REDISMODULE_ATTR; 1029 | REDISMODULE_API RedisModuleString * (*RedisModule_LoadString)(RedisModuleIO *io) REDISMODULE_ATTR; 1030 | REDISMODULE_API char * (*RedisModule_LoadStringBuffer)(RedisModuleIO *io, size_t *lenptr) REDISMODULE_ATTR; 1031 | REDISMODULE_API void (*RedisModule_SaveDouble)(RedisModuleIO *io, double value) REDISMODULE_ATTR; 1032 | REDISMODULE_API double (*RedisModule_LoadDouble)(RedisModuleIO *io) REDISMODULE_ATTR; 1033 | REDISMODULE_API void (*RedisModule_SaveFloat)(RedisModuleIO *io, float value) REDISMODULE_ATTR; 1034 | REDISMODULE_API float (*RedisModule_LoadFloat)(RedisModuleIO *io) REDISMODULE_ATTR; 1035 | REDISMODULE_API void (*RedisModule_SaveLongDouble)(RedisModuleIO *io, long double value) REDISMODULE_ATTR; 1036 | REDISMODULE_API long double (*RedisModule_LoadLongDouble)(RedisModuleIO *io) REDISMODULE_ATTR; 1037 | REDISMODULE_API void * (*RedisModule_LoadDataTypeFromString)(const RedisModuleString *str, const RedisModuleType *mt) REDISMODULE_ATTR; 1038 | REDISMODULE_API void * (*RedisModule_LoadDataTypeFromStringEncver)(const RedisModuleString *str, const RedisModuleType *mt, int encver) REDISMODULE_ATTR; 1039 | REDISMODULE_API RedisModuleString * (*RedisModule_SaveDataTypeToString)(RedisModuleCtx *ctx, void *data, const RedisModuleType *mt) REDISMODULE_ATTR; 1040 | REDISMODULE_API void (*RedisModule_Log)(RedisModuleCtx *ctx, const char *level, const char *fmt, ...) REDISMODULE_ATTR REDISMODULE_ATTR_PRINTF(3,4); 1041 | REDISMODULE_API void (*RedisModule_LogIOError)(RedisModuleIO *io, const char *levelstr, const char *fmt, ...) REDISMODULE_ATTR REDISMODULE_ATTR_PRINTF(3,4); 1042 | REDISMODULE_API void (*RedisModule__Assert)(const char *estr, const char *file, int line) REDISMODULE_ATTR; 1043 | REDISMODULE_API void (*RedisModule_LatencyAddSample)(const char *event, mstime_t latency) REDISMODULE_ATTR; 1044 | REDISMODULE_API int (*RedisModule_StringAppendBuffer)(RedisModuleCtx *ctx, RedisModuleString *str, const char *buf, size_t len) REDISMODULE_ATTR; 1045 | REDISMODULE_API void (*RedisModule_TrimStringAllocation)(RedisModuleString *str) REDISMODULE_ATTR; 1046 | REDISMODULE_API void (*RedisModule_RetainString)(RedisModuleCtx *ctx, RedisModuleString *str) REDISMODULE_ATTR; 1047 | REDISMODULE_API RedisModuleString * (*RedisModule_HoldString)(RedisModuleCtx *ctx, RedisModuleString *str) REDISMODULE_ATTR; 1048 | REDISMODULE_API int (*RedisModule_StringCompare)(RedisModuleString *a, RedisModuleString *b) REDISMODULE_ATTR; 1049 | REDISMODULE_API RedisModuleCtx * (*RedisModule_GetContextFromIO)(RedisModuleIO *io) REDISMODULE_ATTR; 1050 | REDISMODULE_API const RedisModuleString * (*RedisModule_GetKeyNameFromIO)(RedisModuleIO *io) REDISMODULE_ATTR; 1051 | REDISMODULE_API const RedisModuleString * (*RedisModule_GetKeyNameFromModuleKey)(RedisModuleKey *key) REDISMODULE_ATTR; 1052 | REDISMODULE_API int (*RedisModule_GetDbIdFromModuleKey)(RedisModuleKey *key) REDISMODULE_ATTR; 1053 | REDISMODULE_API int (*RedisModule_GetDbIdFromIO)(RedisModuleIO *io) REDISMODULE_ATTR; 1054 | REDISMODULE_API int (*RedisModule_GetDbIdFromOptCtx)(RedisModuleKeyOptCtx *ctx) REDISMODULE_ATTR; 1055 | REDISMODULE_API int (*RedisModule_GetToDbIdFromOptCtx)(RedisModuleKeyOptCtx *ctx) REDISMODULE_ATTR; 1056 | REDISMODULE_API const RedisModuleString * (*RedisModule_GetKeyNameFromOptCtx)(RedisModuleKeyOptCtx *ctx) REDISMODULE_ATTR; 1057 | REDISMODULE_API const RedisModuleString * (*RedisModule_GetToKeyNameFromOptCtx)(RedisModuleKeyOptCtx *ctx) REDISMODULE_ATTR; 1058 | REDISMODULE_API long long (*RedisModule_Milliseconds)(void) REDISMODULE_ATTR; 1059 | REDISMODULE_API uint64_t (*RedisModule_MonotonicMicroseconds)(void) REDISMODULE_ATTR; 1060 | REDISMODULE_API void (*RedisModule_DigestAddStringBuffer)(RedisModuleDigest *md, const char *ele, size_t len) REDISMODULE_ATTR; 1061 | REDISMODULE_API void (*RedisModule_DigestAddLongLong)(RedisModuleDigest *md, long long ele) REDISMODULE_ATTR; 1062 | REDISMODULE_API void (*RedisModule_DigestEndSequence)(RedisModuleDigest *md) REDISMODULE_ATTR; 1063 | REDISMODULE_API int (*RedisModule_GetDbIdFromDigest)(RedisModuleDigest *dig) REDISMODULE_ATTR; 1064 | REDISMODULE_API const RedisModuleString * (*RedisModule_GetKeyNameFromDigest)(RedisModuleDigest *dig) REDISMODULE_ATTR; 1065 | REDISMODULE_API RedisModuleDict * (*RedisModule_CreateDict)(RedisModuleCtx *ctx) REDISMODULE_ATTR; 1066 | REDISMODULE_API void (*RedisModule_FreeDict)(RedisModuleCtx *ctx, RedisModuleDict *d) REDISMODULE_ATTR; 1067 | REDISMODULE_API uint64_t (*RedisModule_DictSize)(RedisModuleDict *d) REDISMODULE_ATTR; 1068 | REDISMODULE_API int (*RedisModule_DictSetC)(RedisModuleDict *d, void *key, size_t keylen, void *ptr) REDISMODULE_ATTR; 1069 | REDISMODULE_API int (*RedisModule_DictReplaceC)(RedisModuleDict *d, void *key, size_t keylen, void *ptr) REDISMODULE_ATTR; 1070 | REDISMODULE_API int (*RedisModule_DictSet)(RedisModuleDict *d, RedisModuleString *key, void *ptr) REDISMODULE_ATTR; 1071 | REDISMODULE_API int (*RedisModule_DictReplace)(RedisModuleDict *d, RedisModuleString *key, void *ptr) REDISMODULE_ATTR; 1072 | REDISMODULE_API void * (*RedisModule_DictGetC)(RedisModuleDict *d, void *key, size_t keylen, int *nokey) REDISMODULE_ATTR; 1073 | REDISMODULE_API void * (*RedisModule_DictGet)(RedisModuleDict *d, RedisModuleString *key, int *nokey) REDISMODULE_ATTR; 1074 | REDISMODULE_API int (*RedisModule_DictDelC)(RedisModuleDict *d, void *key, size_t keylen, void *oldval) REDISMODULE_ATTR; 1075 | REDISMODULE_API int (*RedisModule_DictDel)(RedisModuleDict *d, RedisModuleString *key, void *oldval) REDISMODULE_ATTR; 1076 | REDISMODULE_API RedisModuleDictIter * (*RedisModule_DictIteratorStartC)(RedisModuleDict *d, const char *op, void *key, size_t keylen) REDISMODULE_ATTR; 1077 | REDISMODULE_API RedisModuleDictIter * (*RedisModule_DictIteratorStart)(RedisModuleDict *d, const char *op, RedisModuleString *key) REDISMODULE_ATTR; 1078 | REDISMODULE_API void (*RedisModule_DictIteratorStop)(RedisModuleDictIter *di) REDISMODULE_ATTR; 1079 | REDISMODULE_API int (*RedisModule_DictIteratorReseekC)(RedisModuleDictIter *di, const char *op, void *key, size_t keylen) REDISMODULE_ATTR; 1080 | REDISMODULE_API int (*RedisModule_DictIteratorReseek)(RedisModuleDictIter *di, const char *op, RedisModuleString *key) REDISMODULE_ATTR; 1081 | REDISMODULE_API void * (*RedisModule_DictNextC)(RedisModuleDictIter *di, size_t *keylen, void **dataptr) REDISMODULE_ATTR; 1082 | REDISMODULE_API void * (*RedisModule_DictPrevC)(RedisModuleDictIter *di, size_t *keylen, void **dataptr) REDISMODULE_ATTR; 1083 | REDISMODULE_API RedisModuleString * (*RedisModule_DictNext)(RedisModuleCtx *ctx, RedisModuleDictIter *di, void **dataptr) REDISMODULE_ATTR; 1084 | REDISMODULE_API RedisModuleString * (*RedisModule_DictPrev)(RedisModuleCtx *ctx, RedisModuleDictIter *di, void **dataptr) REDISMODULE_ATTR; 1085 | REDISMODULE_API int (*RedisModule_DictCompareC)(RedisModuleDictIter *di, const char *op, void *key, size_t keylen) REDISMODULE_ATTR; 1086 | REDISMODULE_API int (*RedisModule_DictCompare)(RedisModuleDictIter *di, const char *op, RedisModuleString *key) REDISMODULE_ATTR; 1087 | REDISMODULE_API int (*RedisModule_RegisterInfoFunc)(RedisModuleCtx *ctx, RedisModuleInfoFunc cb) REDISMODULE_ATTR; 1088 | REDISMODULE_API int (*RedisModule_InfoAddSection)(RedisModuleInfoCtx *ctx, const char *name) REDISMODULE_ATTR; 1089 | REDISMODULE_API int (*RedisModule_InfoBeginDictField)(RedisModuleInfoCtx *ctx, const char *name) REDISMODULE_ATTR; 1090 | REDISMODULE_API int (*RedisModule_InfoEndDictField)(RedisModuleInfoCtx *ctx) REDISMODULE_ATTR; 1091 | REDISMODULE_API int (*RedisModule_InfoAddFieldString)(RedisModuleInfoCtx *ctx, const char *field, RedisModuleString *value) REDISMODULE_ATTR; 1092 | REDISMODULE_API int (*RedisModule_InfoAddFieldCString)(RedisModuleInfoCtx *ctx, const char *field,const char *value) REDISMODULE_ATTR; 1093 | REDISMODULE_API int (*RedisModule_InfoAddFieldDouble)(RedisModuleInfoCtx *ctx, const char *field, double value) REDISMODULE_ATTR; 1094 | REDISMODULE_API int (*RedisModule_InfoAddFieldLongLong)(RedisModuleInfoCtx *ctx, const char *field, long long value) REDISMODULE_ATTR; 1095 | REDISMODULE_API int (*RedisModule_InfoAddFieldULongLong)(RedisModuleInfoCtx *ctx, const char *field, unsigned long long value) REDISMODULE_ATTR; 1096 | REDISMODULE_API RedisModuleServerInfoData * (*RedisModule_GetServerInfo)(RedisModuleCtx *ctx, const char *section) REDISMODULE_ATTR; 1097 | REDISMODULE_API void (*RedisModule_FreeServerInfo)(RedisModuleCtx *ctx, RedisModuleServerInfoData *data) REDISMODULE_ATTR; 1098 | REDISMODULE_API RedisModuleString * (*RedisModule_ServerInfoGetField)(RedisModuleCtx *ctx, RedisModuleServerInfoData *data, const char* field) REDISMODULE_ATTR; 1099 | REDISMODULE_API const char * (*RedisModule_ServerInfoGetFieldC)(RedisModuleServerInfoData *data, const char* field) REDISMODULE_ATTR; 1100 | REDISMODULE_API long long (*RedisModule_ServerInfoGetFieldSigned)(RedisModuleServerInfoData *data, const char* field, int *out_err) REDISMODULE_ATTR; 1101 | REDISMODULE_API unsigned long long (*RedisModule_ServerInfoGetFieldUnsigned)(RedisModuleServerInfoData *data, const char* field, int *out_err) REDISMODULE_ATTR; 1102 | REDISMODULE_API double (*RedisModule_ServerInfoGetFieldDouble)(RedisModuleServerInfoData *data, const char* field, int *out_err) REDISMODULE_ATTR; 1103 | REDISMODULE_API int (*RedisModule_SubscribeToServerEvent)(RedisModuleCtx *ctx, RedisModuleEvent event, RedisModuleEventCallback callback) REDISMODULE_ATTR; 1104 | REDISMODULE_API int (*RedisModule_SetLRU)(RedisModuleKey *key, mstime_t lru_idle) REDISMODULE_ATTR; 1105 | REDISMODULE_API int (*RedisModule_GetLRU)(RedisModuleKey *key, mstime_t *lru_idle) REDISMODULE_ATTR; 1106 | REDISMODULE_API int (*RedisModule_SetLFU)(RedisModuleKey *key, long long lfu_freq) REDISMODULE_ATTR; 1107 | REDISMODULE_API int (*RedisModule_GetLFU)(RedisModuleKey *key, long long *lfu_freq) REDISMODULE_ATTR; 1108 | REDISMODULE_API RedisModuleBlockedClient * (*RedisModule_BlockClientOnKeys)(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(RedisModuleCtx*,void*), long long timeout_ms, RedisModuleString **keys, int numkeys, void *privdata) REDISMODULE_ATTR; 1109 | REDISMODULE_API void (*RedisModule_SignalKeyAsReady)(RedisModuleCtx *ctx, RedisModuleString *key) REDISMODULE_ATTR; 1110 | REDISMODULE_API RedisModuleString * (*RedisModule_GetBlockedClientReadyKey)(RedisModuleCtx *ctx) REDISMODULE_ATTR; 1111 | REDISMODULE_API RedisModuleScanCursor * (*RedisModule_ScanCursorCreate)() REDISMODULE_ATTR; 1112 | REDISMODULE_API void (*RedisModule_ScanCursorRestart)(RedisModuleScanCursor *cursor) REDISMODULE_ATTR; 1113 | REDISMODULE_API void (*RedisModule_ScanCursorDestroy)(RedisModuleScanCursor *cursor) REDISMODULE_ATTR; 1114 | REDISMODULE_API int (*RedisModule_Scan)(RedisModuleCtx *ctx, RedisModuleScanCursor *cursor, RedisModuleScanCB fn, void *privdata) REDISMODULE_ATTR; 1115 | REDISMODULE_API int (*RedisModule_ScanKey)(RedisModuleKey *key, RedisModuleScanCursor *cursor, RedisModuleScanKeyCB fn, void *privdata) REDISMODULE_ATTR; 1116 | REDISMODULE_API int (*RedisModule_GetContextFlagsAll)() REDISMODULE_ATTR; 1117 | REDISMODULE_API int (*RedisModule_GetKeyspaceNotificationFlagsAll)() REDISMODULE_ATTR; 1118 | REDISMODULE_API int (*RedisModule_IsSubEventSupported)(RedisModuleEvent event, uint64_t subevent) REDISMODULE_ATTR; 1119 | REDISMODULE_API int (*RedisModule_GetServerVersion)() REDISMODULE_ATTR; 1120 | REDISMODULE_API int (*RedisModule_GetTypeMethodVersion)() REDISMODULE_ATTR; 1121 | REDISMODULE_API void (*RedisModule_Yield)(RedisModuleCtx *ctx, int flags, const char *busy_reply) REDISMODULE_ATTR; 1122 | REDISMODULE_API RedisModuleBlockedClient * (*RedisModule_BlockClient)(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(RedisModuleCtx*,void*), long long timeout_ms) REDISMODULE_ATTR; 1123 | REDISMODULE_API int (*RedisModule_UnblockClient)(RedisModuleBlockedClient *bc, void *privdata) REDISMODULE_ATTR; 1124 | REDISMODULE_API int (*RedisModule_IsBlockedReplyRequest)(RedisModuleCtx *ctx) REDISMODULE_ATTR; 1125 | REDISMODULE_API int (*RedisModule_IsBlockedTimeoutRequest)(RedisModuleCtx *ctx) REDISMODULE_ATTR; 1126 | REDISMODULE_API void * (*RedisModule_GetBlockedClientPrivateData)(RedisModuleCtx *ctx) REDISMODULE_ATTR; 1127 | REDISMODULE_API RedisModuleBlockedClient * (*RedisModule_GetBlockedClientHandle)(RedisModuleCtx *ctx) REDISMODULE_ATTR; 1128 | REDISMODULE_API int (*RedisModule_AbortBlock)(RedisModuleBlockedClient *bc) REDISMODULE_ATTR; 1129 | REDISMODULE_API int (*RedisModule_BlockedClientMeasureTimeStart)(RedisModuleBlockedClient *bc) REDISMODULE_ATTR; 1130 | REDISMODULE_API int (*RedisModule_BlockedClientMeasureTimeEnd)(RedisModuleBlockedClient *bc) REDISMODULE_ATTR; 1131 | REDISMODULE_API RedisModuleCtx * (*RedisModule_GetThreadSafeContext)(RedisModuleBlockedClient *bc) REDISMODULE_ATTR; 1132 | REDISMODULE_API RedisModuleCtx * (*RedisModule_GetDetachedThreadSafeContext)(RedisModuleCtx *ctx) REDISMODULE_ATTR; 1133 | REDISMODULE_API void (*RedisModule_FreeThreadSafeContext)(RedisModuleCtx *ctx) REDISMODULE_ATTR; 1134 | REDISMODULE_API void (*RedisModule_ThreadSafeContextLock)(RedisModuleCtx *ctx) REDISMODULE_ATTR; 1135 | REDISMODULE_API int (*RedisModule_ThreadSafeContextTryLock)(RedisModuleCtx *ctx) REDISMODULE_ATTR; 1136 | REDISMODULE_API void (*RedisModule_ThreadSafeContextUnlock)(RedisModuleCtx *ctx) REDISMODULE_ATTR; 1137 | REDISMODULE_API int (*RedisModule_SubscribeToKeyspaceEvents)(RedisModuleCtx *ctx, int types, RedisModuleNotificationFunc cb) REDISMODULE_ATTR; 1138 | REDISMODULE_API int (*RedisModule_NotifyKeyspaceEvent)(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key) REDISMODULE_ATTR; 1139 | REDISMODULE_API int (*RedisModule_GetNotifyKeyspaceEvents)() REDISMODULE_ATTR; 1140 | REDISMODULE_API int (*RedisModule_BlockedClientDisconnected)(RedisModuleCtx *ctx) REDISMODULE_ATTR; 1141 | REDISMODULE_API void (*RedisModule_RegisterClusterMessageReceiver)(RedisModuleCtx *ctx, uint8_t type, RedisModuleClusterMessageReceiver callback) REDISMODULE_ATTR; 1142 | REDISMODULE_API int (*RedisModule_SendClusterMessage)(RedisModuleCtx *ctx, const char *target_id, uint8_t type, const char *msg, uint32_t len) REDISMODULE_ATTR; 1143 | REDISMODULE_API int (*RedisModule_GetClusterNodeInfo)(RedisModuleCtx *ctx, const char *id, char *ip, char *master_id, int *port, int *flags) REDISMODULE_ATTR; 1144 | REDISMODULE_API char ** (*RedisModule_GetClusterNodesList)(RedisModuleCtx *ctx, size_t *numnodes) REDISMODULE_ATTR; 1145 | REDISMODULE_API void (*RedisModule_FreeClusterNodesList)(char **ids) REDISMODULE_ATTR; 1146 | REDISMODULE_API RedisModuleTimerID (*RedisModule_CreateTimer)(RedisModuleCtx *ctx, mstime_t period, RedisModuleTimerProc callback, void *data) REDISMODULE_ATTR; 1147 | REDISMODULE_API int (*RedisModule_StopTimer)(RedisModuleCtx *ctx, RedisModuleTimerID id, void **data) REDISMODULE_ATTR; 1148 | REDISMODULE_API int (*RedisModule_GetTimerInfo)(RedisModuleCtx *ctx, RedisModuleTimerID id, uint64_t *remaining, void **data) REDISMODULE_ATTR; 1149 | REDISMODULE_API const char * (*RedisModule_GetMyClusterID)(void) REDISMODULE_ATTR; 1150 | REDISMODULE_API size_t (*RedisModule_GetClusterSize)(void) REDISMODULE_ATTR; 1151 | REDISMODULE_API void (*RedisModule_GetRandomBytes)(unsigned char *dst, size_t len) REDISMODULE_ATTR; 1152 | REDISMODULE_API void (*RedisModule_GetRandomHexChars)(char *dst, size_t len) REDISMODULE_ATTR; 1153 | REDISMODULE_API void (*RedisModule_SetDisconnectCallback)(RedisModuleBlockedClient *bc, RedisModuleDisconnectFunc callback) REDISMODULE_ATTR; 1154 | REDISMODULE_API void (*RedisModule_SetClusterFlags)(RedisModuleCtx *ctx, uint64_t flags) REDISMODULE_ATTR; 1155 | REDISMODULE_API int (*RedisModule_ExportSharedAPI)(RedisModuleCtx *ctx, const char *apiname, void *func) REDISMODULE_ATTR; 1156 | REDISMODULE_API void * (*RedisModule_GetSharedAPI)(RedisModuleCtx *ctx, const char *apiname) REDISMODULE_ATTR; 1157 | REDISMODULE_API RedisModuleCommandFilter * (*RedisModule_RegisterCommandFilter)(RedisModuleCtx *ctx, RedisModuleCommandFilterFunc cb, int flags) REDISMODULE_ATTR; 1158 | REDISMODULE_API int (*RedisModule_UnregisterCommandFilter)(RedisModuleCtx *ctx, RedisModuleCommandFilter *filter) REDISMODULE_ATTR; 1159 | REDISMODULE_API int (*RedisModule_CommandFilterArgsCount)(RedisModuleCommandFilterCtx *fctx) REDISMODULE_ATTR; 1160 | REDISMODULE_API RedisModuleString * (*RedisModule_CommandFilterArgGet)(RedisModuleCommandFilterCtx *fctx, int pos) REDISMODULE_ATTR; 1161 | REDISMODULE_API int (*RedisModule_CommandFilterArgInsert)(RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg) REDISMODULE_ATTR; 1162 | REDISMODULE_API int (*RedisModule_CommandFilterArgReplace)(RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg) REDISMODULE_ATTR; 1163 | REDISMODULE_API int (*RedisModule_CommandFilterArgDelete)(RedisModuleCommandFilterCtx *fctx, int pos) REDISMODULE_ATTR; 1164 | REDISMODULE_API int (*RedisModule_Fork)(RedisModuleForkDoneHandler cb, void *user_data) REDISMODULE_ATTR; 1165 | REDISMODULE_API void (*RedisModule_SendChildHeartbeat)(double progress) REDISMODULE_ATTR; 1166 | REDISMODULE_API int (*RedisModule_ExitFromChild)(int retcode) REDISMODULE_ATTR; 1167 | REDISMODULE_API int (*RedisModule_KillForkChild)(int child_pid) REDISMODULE_ATTR; 1168 | REDISMODULE_API float (*RedisModule_GetUsedMemoryRatio)() REDISMODULE_ATTR; 1169 | REDISMODULE_API size_t (*RedisModule_MallocSize)(void* ptr) REDISMODULE_ATTR; 1170 | REDISMODULE_API size_t (*RedisModule_MallocUsableSize)(void *ptr) REDISMODULE_ATTR; 1171 | REDISMODULE_API size_t (*RedisModule_MallocSizeString)(RedisModuleString* str) REDISMODULE_ATTR; 1172 | REDISMODULE_API size_t (*RedisModule_MallocSizeDict)(RedisModuleDict* dict) REDISMODULE_ATTR; 1173 | REDISMODULE_API RedisModuleUser * (*RedisModule_CreateModuleUser)(const char *name) REDISMODULE_ATTR; 1174 | REDISMODULE_API void (*RedisModule_FreeModuleUser)(RedisModuleUser *user) REDISMODULE_ATTR; 1175 | REDISMODULE_API int (*RedisModule_SetModuleUserACL)(RedisModuleUser *user, const char* acl) REDISMODULE_ATTR; 1176 | REDISMODULE_API RedisModuleString * (*RedisModule_GetCurrentUserName)(RedisModuleCtx *ctx) REDISMODULE_ATTR; 1177 | REDISMODULE_API RedisModuleUser * (*RedisModule_GetModuleUserFromUserName)(RedisModuleString *name) REDISMODULE_ATTR; 1178 | REDISMODULE_API int (*RedisModule_ACLCheckCommandPermissions)(RedisModuleUser *user, RedisModuleString **argv, int argc) REDISMODULE_ATTR; 1179 | REDISMODULE_API int (*RedisModule_ACLCheckKeyPermissions)(RedisModuleUser *user, RedisModuleString *key, int flags) REDISMODULE_ATTR; 1180 | REDISMODULE_API int (*RedisModule_ACLCheckChannelPermissions)(RedisModuleUser *user, RedisModuleString *ch, int literal) REDISMODULE_ATTR; 1181 | REDISMODULE_API void (*RedisModule_ACLAddLogEntry)(RedisModuleCtx *ctx, RedisModuleUser *user, RedisModuleString *object, RedisModuleACLLogEntryReason reason) REDISMODULE_ATTR; 1182 | REDISMODULE_API int (*RedisModule_AuthenticateClientWithACLUser)(RedisModuleCtx *ctx, const char *name, size_t len, RedisModuleUserChangedFunc callback, void *privdata, uint64_t *client_id) REDISMODULE_ATTR; 1183 | REDISMODULE_API int (*RedisModule_AuthenticateClientWithUser)(RedisModuleCtx *ctx, RedisModuleUser *user, RedisModuleUserChangedFunc callback, void *privdata, uint64_t *client_id) REDISMODULE_ATTR; 1184 | REDISMODULE_API int (*RedisModule_DeauthenticateAndCloseClient)(RedisModuleCtx *ctx, uint64_t client_id) REDISMODULE_ATTR; 1185 | REDISMODULE_API int (*RedisModule_RedactClientCommandArgument)(RedisModuleCtx *ctx, int pos) REDISMODULE_ATTR; 1186 | REDISMODULE_API RedisModuleString * (*RedisModule_GetClientCertificate)(RedisModuleCtx *ctx, uint64_t id) REDISMODULE_ATTR; 1187 | REDISMODULE_API int *(*RedisModule_GetCommandKeys)(RedisModuleCtx *ctx, RedisModuleString **argv, int argc, int *num_keys) REDISMODULE_ATTR; 1188 | REDISMODULE_API int *(*RedisModule_GetCommandKeysWithFlags)(RedisModuleCtx *ctx, RedisModuleString **argv, int argc, int *num_keys, int **out_flags) REDISMODULE_ATTR; 1189 | REDISMODULE_API const char *(*RedisModule_GetCurrentCommandName)(RedisModuleCtx *ctx) REDISMODULE_ATTR; 1190 | REDISMODULE_API int (*RedisModule_RegisterDefragFunc)(RedisModuleCtx *ctx, RedisModuleDefragFunc func) REDISMODULE_ATTR; 1191 | REDISMODULE_API void *(*RedisModule_DefragAlloc)(RedisModuleDefragCtx *ctx, void *ptr) REDISMODULE_ATTR; 1192 | REDISMODULE_API RedisModuleString *(*RedisModule_DefragRedisModuleString)(RedisModuleDefragCtx *ctx, RedisModuleString *str) REDISMODULE_ATTR; 1193 | REDISMODULE_API int (*RedisModule_DefragShouldStop)(RedisModuleDefragCtx *ctx) REDISMODULE_ATTR; 1194 | REDISMODULE_API int (*RedisModule_DefragCursorSet)(RedisModuleDefragCtx *ctx, unsigned long cursor) REDISMODULE_ATTR; 1195 | REDISMODULE_API int (*RedisModule_DefragCursorGet)(RedisModuleDefragCtx *ctx, unsigned long *cursor) REDISMODULE_ATTR; 1196 | REDISMODULE_API int (*RedisModule_GetDbIdFromDefragCtx)(RedisModuleDefragCtx *ctx) REDISMODULE_ATTR; 1197 | REDISMODULE_API const RedisModuleString * (*RedisModule_GetKeyNameFromDefragCtx)(RedisModuleDefragCtx *ctx) REDISMODULE_ATTR; 1198 | REDISMODULE_API int (*RedisModule_EventLoopAdd)(int fd, int mask, RedisModuleEventLoopFunc func, void *user_data) REDISMODULE_ATTR; 1199 | REDISMODULE_API int (*RedisModule_EventLoopDel)(int fd, int mask) REDISMODULE_ATTR; 1200 | REDISMODULE_API int (*RedisModule_EventLoopAddOneShot)(RedisModuleEventLoopOneShotFunc func, void *user_data) REDISMODULE_ATTR; 1201 | REDISMODULE_API int (*RedisModule_RegisterBoolConfig)(RedisModuleCtx *ctx, const char *name, int default_val, unsigned int flags, RedisModuleConfigGetBoolFunc getfn, RedisModuleConfigSetBoolFunc setfn, RedisModuleConfigApplyFunc applyfn, void *privdata) REDISMODULE_ATTR; 1202 | REDISMODULE_API int (*RedisModule_RegisterNumericConfig)(RedisModuleCtx *ctx, const char *name, long long default_val, unsigned int flags, long long min, long long max, RedisModuleConfigGetNumericFunc getfn, RedisModuleConfigSetNumericFunc setfn, RedisModuleConfigApplyFunc applyfn, void *privdata) REDISMODULE_ATTR; 1203 | REDISMODULE_API int (*RedisModule_RegisterStringConfig)(RedisModuleCtx *ctx, const char *name, const char *default_val, unsigned int flags, RedisModuleConfigGetStringFunc getfn, RedisModuleConfigSetStringFunc setfn, RedisModuleConfigApplyFunc applyfn, void *privdata) REDISMODULE_ATTR; 1204 | REDISMODULE_API int (*RedisModule_RegisterEnumConfig)(RedisModuleCtx *ctx, const char *name, int default_val, unsigned int flags, const char **enum_values, const int *int_values, int num_enum_vals, RedisModuleConfigGetEnumFunc getfn, RedisModuleConfigSetEnumFunc setfn, RedisModuleConfigApplyFunc applyfn, void *privdata) REDISMODULE_ATTR; 1205 | REDISMODULE_API int (*RedisModule_LoadConfigs)(RedisModuleCtx *ctx) REDISMODULE_ATTR; 1206 | 1207 | #define RedisModule_IsAOFClient(id) ((id) == UINT64_MAX) 1208 | 1209 | /* This is included inline inside each Redis module. */ 1210 | static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver) REDISMODULE_ATTR_UNUSED; 1211 | static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver) { 1212 | void *getapifuncptr = ((void**)ctx)[0]; 1213 | RedisModule_GetApi = (int (*)(const char *, void *)) (unsigned long)getapifuncptr; 1214 | REDISMODULE_GET_API(Alloc); 1215 | REDISMODULE_GET_API(TryAlloc); 1216 | REDISMODULE_GET_API(Calloc); 1217 | REDISMODULE_GET_API(Free); 1218 | REDISMODULE_GET_API(Realloc); 1219 | REDISMODULE_GET_API(Strdup); 1220 | REDISMODULE_GET_API(CreateCommand); 1221 | REDISMODULE_GET_API(GetCommand); 1222 | REDISMODULE_GET_API(CreateSubcommand); 1223 | REDISMODULE_GET_API(SetCommandInfo); 1224 | REDISMODULE_GET_API(SetModuleAttribs); 1225 | REDISMODULE_GET_API(IsModuleNameBusy); 1226 | REDISMODULE_GET_API(WrongArity); 1227 | REDISMODULE_GET_API(ReplyWithLongLong); 1228 | REDISMODULE_GET_API(ReplyWithError); 1229 | REDISMODULE_GET_API(ReplyWithSimpleString); 1230 | REDISMODULE_GET_API(ReplyWithArray); 1231 | REDISMODULE_GET_API(ReplyWithMap); 1232 | REDISMODULE_GET_API(ReplyWithSet); 1233 | REDISMODULE_GET_API(ReplyWithAttribute); 1234 | REDISMODULE_GET_API(ReplyWithNullArray); 1235 | REDISMODULE_GET_API(ReplyWithEmptyArray); 1236 | REDISMODULE_GET_API(ReplySetArrayLength); 1237 | REDISMODULE_GET_API(ReplySetMapLength); 1238 | REDISMODULE_GET_API(ReplySetSetLength); 1239 | REDISMODULE_GET_API(ReplySetAttributeLength); 1240 | REDISMODULE_GET_API(ReplySetPushLength); 1241 | REDISMODULE_GET_API(ReplyWithStringBuffer); 1242 | REDISMODULE_GET_API(ReplyWithCString); 1243 | REDISMODULE_GET_API(ReplyWithString); 1244 | REDISMODULE_GET_API(ReplyWithEmptyString); 1245 | REDISMODULE_GET_API(ReplyWithVerbatimString); 1246 | REDISMODULE_GET_API(ReplyWithVerbatimStringType); 1247 | REDISMODULE_GET_API(ReplyWithNull); 1248 | REDISMODULE_GET_API(ReplyWithBool); 1249 | REDISMODULE_GET_API(ReplyWithCallReply); 1250 | REDISMODULE_GET_API(ReplyWithDouble); 1251 | REDISMODULE_GET_API(ReplyWithBigNumber); 1252 | REDISMODULE_GET_API(ReplyWithLongDouble); 1253 | REDISMODULE_GET_API(GetSelectedDb); 1254 | REDISMODULE_GET_API(SelectDb); 1255 | REDISMODULE_GET_API(KeyExists); 1256 | REDISMODULE_GET_API(OpenKey); 1257 | REDISMODULE_GET_API(CloseKey); 1258 | REDISMODULE_GET_API(KeyType); 1259 | REDISMODULE_GET_API(ValueLength); 1260 | REDISMODULE_GET_API(ListPush); 1261 | REDISMODULE_GET_API(ListPop); 1262 | REDISMODULE_GET_API(ListGet); 1263 | REDISMODULE_GET_API(ListSet); 1264 | REDISMODULE_GET_API(ListInsert); 1265 | REDISMODULE_GET_API(ListDelete); 1266 | REDISMODULE_GET_API(StringToLongLong); 1267 | REDISMODULE_GET_API(StringToULongLong); 1268 | REDISMODULE_GET_API(StringToDouble); 1269 | REDISMODULE_GET_API(StringToLongDouble); 1270 | REDISMODULE_GET_API(StringToStreamID); 1271 | REDISMODULE_GET_API(Call); 1272 | REDISMODULE_GET_API(CallReplyProto); 1273 | REDISMODULE_GET_API(FreeCallReply); 1274 | REDISMODULE_GET_API(CallReplyInteger); 1275 | REDISMODULE_GET_API(CallReplyDouble); 1276 | REDISMODULE_GET_API(CallReplyBool); 1277 | REDISMODULE_GET_API(CallReplyBigNumber); 1278 | REDISMODULE_GET_API(CallReplyVerbatim); 1279 | REDISMODULE_GET_API(CallReplySetElement); 1280 | REDISMODULE_GET_API(CallReplyMapElement); 1281 | REDISMODULE_GET_API(CallReplyAttributeElement); 1282 | REDISMODULE_GET_API(CallReplyAttribute); 1283 | REDISMODULE_GET_API(CallReplyType); 1284 | REDISMODULE_GET_API(CallReplyLength); 1285 | REDISMODULE_GET_API(CallReplyArrayElement); 1286 | REDISMODULE_GET_API(CallReplyStringPtr); 1287 | REDISMODULE_GET_API(CreateStringFromCallReply); 1288 | REDISMODULE_GET_API(CreateString); 1289 | REDISMODULE_GET_API(CreateStringFromLongLong); 1290 | REDISMODULE_GET_API(CreateStringFromULongLong); 1291 | REDISMODULE_GET_API(CreateStringFromDouble); 1292 | REDISMODULE_GET_API(CreateStringFromLongDouble); 1293 | REDISMODULE_GET_API(CreateStringFromString); 1294 | REDISMODULE_GET_API(CreateStringFromStreamID); 1295 | REDISMODULE_GET_API(CreateStringPrintf); 1296 | REDISMODULE_GET_API(FreeString); 1297 | REDISMODULE_GET_API(StringPtrLen); 1298 | REDISMODULE_GET_API(AutoMemory); 1299 | REDISMODULE_GET_API(Replicate); 1300 | REDISMODULE_GET_API(ReplicateVerbatim); 1301 | REDISMODULE_GET_API(DeleteKey); 1302 | REDISMODULE_GET_API(UnlinkKey); 1303 | REDISMODULE_GET_API(StringSet); 1304 | REDISMODULE_GET_API(StringDMA); 1305 | REDISMODULE_GET_API(StringTruncate); 1306 | REDISMODULE_GET_API(GetExpire); 1307 | REDISMODULE_GET_API(SetExpire); 1308 | REDISMODULE_GET_API(GetAbsExpire); 1309 | REDISMODULE_GET_API(SetAbsExpire); 1310 | REDISMODULE_GET_API(ResetDataset); 1311 | REDISMODULE_GET_API(DbSize); 1312 | REDISMODULE_GET_API(RandomKey); 1313 | REDISMODULE_GET_API(ZsetAdd); 1314 | REDISMODULE_GET_API(ZsetIncrby); 1315 | REDISMODULE_GET_API(ZsetScore); 1316 | REDISMODULE_GET_API(ZsetRem); 1317 | REDISMODULE_GET_API(ZsetRangeStop); 1318 | REDISMODULE_GET_API(ZsetFirstInScoreRange); 1319 | REDISMODULE_GET_API(ZsetLastInScoreRange); 1320 | REDISMODULE_GET_API(ZsetFirstInLexRange); 1321 | REDISMODULE_GET_API(ZsetLastInLexRange); 1322 | REDISMODULE_GET_API(ZsetRangeCurrentElement); 1323 | REDISMODULE_GET_API(ZsetRangeNext); 1324 | REDISMODULE_GET_API(ZsetRangePrev); 1325 | REDISMODULE_GET_API(ZsetRangeEndReached); 1326 | REDISMODULE_GET_API(HashSet); 1327 | REDISMODULE_GET_API(HashGet); 1328 | REDISMODULE_GET_API(StreamAdd); 1329 | REDISMODULE_GET_API(StreamDelete); 1330 | REDISMODULE_GET_API(StreamIteratorStart); 1331 | REDISMODULE_GET_API(StreamIteratorStop); 1332 | REDISMODULE_GET_API(StreamIteratorNextID); 1333 | REDISMODULE_GET_API(StreamIteratorNextField); 1334 | REDISMODULE_GET_API(StreamIteratorDelete); 1335 | REDISMODULE_GET_API(StreamTrimByLength); 1336 | REDISMODULE_GET_API(StreamTrimByID); 1337 | REDISMODULE_GET_API(IsKeysPositionRequest); 1338 | REDISMODULE_GET_API(KeyAtPos); 1339 | REDISMODULE_GET_API(KeyAtPosWithFlags); 1340 | REDISMODULE_GET_API(IsChannelsPositionRequest); 1341 | REDISMODULE_GET_API(ChannelAtPosWithFlags); 1342 | REDISMODULE_GET_API(GetClientId); 1343 | REDISMODULE_GET_API(GetClientUserNameById); 1344 | REDISMODULE_GET_API(GetContextFlags); 1345 | REDISMODULE_GET_API(AvoidReplicaTraffic); 1346 | REDISMODULE_GET_API(PoolAlloc); 1347 | REDISMODULE_GET_API(CreateDataType); 1348 | REDISMODULE_GET_API(ModuleTypeSetValue); 1349 | REDISMODULE_GET_API(ModuleTypeReplaceValue); 1350 | REDISMODULE_GET_API(ModuleTypeGetType); 1351 | REDISMODULE_GET_API(ModuleTypeGetValue); 1352 | REDISMODULE_GET_API(IsIOError); 1353 | REDISMODULE_GET_API(SetModuleOptions); 1354 | REDISMODULE_GET_API(SignalModifiedKey); 1355 | REDISMODULE_GET_API(SaveUnsigned); 1356 | REDISMODULE_GET_API(LoadUnsigned); 1357 | REDISMODULE_GET_API(SaveSigned); 1358 | REDISMODULE_GET_API(LoadSigned); 1359 | REDISMODULE_GET_API(SaveString); 1360 | REDISMODULE_GET_API(SaveStringBuffer); 1361 | REDISMODULE_GET_API(LoadString); 1362 | REDISMODULE_GET_API(LoadStringBuffer); 1363 | REDISMODULE_GET_API(SaveDouble); 1364 | REDISMODULE_GET_API(LoadDouble); 1365 | REDISMODULE_GET_API(SaveFloat); 1366 | REDISMODULE_GET_API(LoadFloat); 1367 | REDISMODULE_GET_API(SaveLongDouble); 1368 | REDISMODULE_GET_API(LoadLongDouble); 1369 | REDISMODULE_GET_API(SaveDataTypeToString); 1370 | REDISMODULE_GET_API(LoadDataTypeFromString); 1371 | REDISMODULE_GET_API(LoadDataTypeFromStringEncver); 1372 | REDISMODULE_GET_API(EmitAOF); 1373 | REDISMODULE_GET_API(Log); 1374 | REDISMODULE_GET_API(LogIOError); 1375 | REDISMODULE_GET_API(_Assert); 1376 | REDISMODULE_GET_API(LatencyAddSample); 1377 | REDISMODULE_GET_API(StringAppendBuffer); 1378 | REDISMODULE_GET_API(TrimStringAllocation); 1379 | REDISMODULE_GET_API(RetainString); 1380 | REDISMODULE_GET_API(HoldString); 1381 | REDISMODULE_GET_API(StringCompare); 1382 | REDISMODULE_GET_API(GetContextFromIO); 1383 | REDISMODULE_GET_API(GetKeyNameFromIO); 1384 | REDISMODULE_GET_API(GetKeyNameFromModuleKey); 1385 | REDISMODULE_GET_API(GetDbIdFromModuleKey); 1386 | REDISMODULE_GET_API(GetDbIdFromIO); 1387 | REDISMODULE_GET_API(GetKeyNameFromOptCtx); 1388 | REDISMODULE_GET_API(GetToKeyNameFromOptCtx); 1389 | REDISMODULE_GET_API(GetDbIdFromOptCtx); 1390 | REDISMODULE_GET_API(GetToDbIdFromOptCtx); 1391 | REDISMODULE_GET_API(Milliseconds); 1392 | REDISMODULE_GET_API(MonotonicMicroseconds); 1393 | REDISMODULE_GET_API(DigestAddStringBuffer); 1394 | REDISMODULE_GET_API(DigestAddLongLong); 1395 | REDISMODULE_GET_API(DigestEndSequence); 1396 | REDISMODULE_GET_API(GetKeyNameFromDigest); 1397 | REDISMODULE_GET_API(GetDbIdFromDigest); 1398 | REDISMODULE_GET_API(CreateDict); 1399 | REDISMODULE_GET_API(FreeDict); 1400 | REDISMODULE_GET_API(DictSize); 1401 | REDISMODULE_GET_API(DictSetC); 1402 | REDISMODULE_GET_API(DictReplaceC); 1403 | REDISMODULE_GET_API(DictSet); 1404 | REDISMODULE_GET_API(DictReplace); 1405 | REDISMODULE_GET_API(DictGetC); 1406 | REDISMODULE_GET_API(DictGet); 1407 | REDISMODULE_GET_API(DictDelC); 1408 | REDISMODULE_GET_API(DictDel); 1409 | REDISMODULE_GET_API(DictIteratorStartC); 1410 | REDISMODULE_GET_API(DictIteratorStart); 1411 | REDISMODULE_GET_API(DictIteratorStop); 1412 | REDISMODULE_GET_API(DictIteratorReseekC); 1413 | REDISMODULE_GET_API(DictIteratorReseek); 1414 | REDISMODULE_GET_API(DictNextC); 1415 | REDISMODULE_GET_API(DictPrevC); 1416 | REDISMODULE_GET_API(DictNext); 1417 | REDISMODULE_GET_API(DictPrev); 1418 | REDISMODULE_GET_API(DictCompare); 1419 | REDISMODULE_GET_API(DictCompareC); 1420 | REDISMODULE_GET_API(RegisterInfoFunc); 1421 | REDISMODULE_GET_API(InfoAddSection); 1422 | REDISMODULE_GET_API(InfoBeginDictField); 1423 | REDISMODULE_GET_API(InfoEndDictField); 1424 | REDISMODULE_GET_API(InfoAddFieldString); 1425 | REDISMODULE_GET_API(InfoAddFieldCString); 1426 | REDISMODULE_GET_API(InfoAddFieldDouble); 1427 | REDISMODULE_GET_API(InfoAddFieldLongLong); 1428 | REDISMODULE_GET_API(InfoAddFieldULongLong); 1429 | REDISMODULE_GET_API(GetServerInfo); 1430 | REDISMODULE_GET_API(FreeServerInfo); 1431 | REDISMODULE_GET_API(ServerInfoGetField); 1432 | REDISMODULE_GET_API(ServerInfoGetFieldC); 1433 | REDISMODULE_GET_API(ServerInfoGetFieldSigned); 1434 | REDISMODULE_GET_API(ServerInfoGetFieldUnsigned); 1435 | REDISMODULE_GET_API(ServerInfoGetFieldDouble); 1436 | REDISMODULE_GET_API(GetClientInfoById); 1437 | REDISMODULE_GET_API(GetClientNameById); 1438 | REDISMODULE_GET_API(SetClientNameById); 1439 | REDISMODULE_GET_API(PublishMessage); 1440 | REDISMODULE_GET_API(PublishMessageShard); 1441 | REDISMODULE_GET_API(SubscribeToServerEvent); 1442 | REDISMODULE_GET_API(SetLRU); 1443 | REDISMODULE_GET_API(GetLRU); 1444 | REDISMODULE_GET_API(SetLFU); 1445 | REDISMODULE_GET_API(GetLFU); 1446 | REDISMODULE_GET_API(BlockClientOnKeys); 1447 | REDISMODULE_GET_API(SignalKeyAsReady); 1448 | REDISMODULE_GET_API(GetBlockedClientReadyKey); 1449 | REDISMODULE_GET_API(ScanCursorCreate); 1450 | REDISMODULE_GET_API(ScanCursorRestart); 1451 | REDISMODULE_GET_API(ScanCursorDestroy); 1452 | REDISMODULE_GET_API(Scan); 1453 | REDISMODULE_GET_API(ScanKey); 1454 | REDISMODULE_GET_API(GetContextFlagsAll); 1455 | REDISMODULE_GET_API(GetKeyspaceNotificationFlagsAll); 1456 | REDISMODULE_GET_API(IsSubEventSupported); 1457 | REDISMODULE_GET_API(GetServerVersion); 1458 | REDISMODULE_GET_API(GetTypeMethodVersion); 1459 | REDISMODULE_GET_API(Yield); 1460 | REDISMODULE_GET_API(GetThreadSafeContext); 1461 | REDISMODULE_GET_API(GetDetachedThreadSafeContext); 1462 | REDISMODULE_GET_API(FreeThreadSafeContext); 1463 | REDISMODULE_GET_API(ThreadSafeContextLock); 1464 | REDISMODULE_GET_API(ThreadSafeContextTryLock); 1465 | REDISMODULE_GET_API(ThreadSafeContextUnlock); 1466 | REDISMODULE_GET_API(BlockClient); 1467 | REDISMODULE_GET_API(UnblockClient); 1468 | REDISMODULE_GET_API(IsBlockedReplyRequest); 1469 | REDISMODULE_GET_API(IsBlockedTimeoutRequest); 1470 | REDISMODULE_GET_API(GetBlockedClientPrivateData); 1471 | REDISMODULE_GET_API(GetBlockedClientHandle); 1472 | REDISMODULE_GET_API(AbortBlock); 1473 | REDISMODULE_GET_API(BlockedClientMeasureTimeStart); 1474 | REDISMODULE_GET_API(BlockedClientMeasureTimeEnd); 1475 | REDISMODULE_GET_API(SetDisconnectCallback); 1476 | REDISMODULE_GET_API(SubscribeToKeyspaceEvents); 1477 | REDISMODULE_GET_API(NotifyKeyspaceEvent); 1478 | REDISMODULE_GET_API(GetNotifyKeyspaceEvents); 1479 | REDISMODULE_GET_API(BlockedClientDisconnected); 1480 | REDISMODULE_GET_API(RegisterClusterMessageReceiver); 1481 | REDISMODULE_GET_API(SendClusterMessage); 1482 | REDISMODULE_GET_API(GetClusterNodeInfo); 1483 | REDISMODULE_GET_API(GetClusterNodesList); 1484 | REDISMODULE_GET_API(FreeClusterNodesList); 1485 | REDISMODULE_GET_API(CreateTimer); 1486 | REDISMODULE_GET_API(StopTimer); 1487 | REDISMODULE_GET_API(GetTimerInfo); 1488 | REDISMODULE_GET_API(GetMyClusterID); 1489 | REDISMODULE_GET_API(GetClusterSize); 1490 | REDISMODULE_GET_API(GetRandomBytes); 1491 | REDISMODULE_GET_API(GetRandomHexChars); 1492 | REDISMODULE_GET_API(SetClusterFlags); 1493 | REDISMODULE_GET_API(ExportSharedAPI); 1494 | REDISMODULE_GET_API(GetSharedAPI); 1495 | REDISMODULE_GET_API(RegisterCommandFilter); 1496 | REDISMODULE_GET_API(UnregisterCommandFilter); 1497 | REDISMODULE_GET_API(CommandFilterArgsCount); 1498 | REDISMODULE_GET_API(CommandFilterArgGet); 1499 | REDISMODULE_GET_API(CommandFilterArgInsert); 1500 | REDISMODULE_GET_API(CommandFilterArgReplace); 1501 | REDISMODULE_GET_API(CommandFilterArgDelete); 1502 | REDISMODULE_GET_API(Fork); 1503 | REDISMODULE_GET_API(SendChildHeartbeat); 1504 | REDISMODULE_GET_API(ExitFromChild); 1505 | REDISMODULE_GET_API(KillForkChild); 1506 | REDISMODULE_GET_API(GetUsedMemoryRatio); 1507 | REDISMODULE_GET_API(MallocSize); 1508 | REDISMODULE_GET_API(MallocUsableSize); 1509 | REDISMODULE_GET_API(MallocSizeString); 1510 | REDISMODULE_GET_API(MallocSizeDict); 1511 | REDISMODULE_GET_API(CreateModuleUser); 1512 | REDISMODULE_GET_API(FreeModuleUser); 1513 | REDISMODULE_GET_API(SetModuleUserACL); 1514 | REDISMODULE_GET_API(GetCurrentUserName); 1515 | REDISMODULE_GET_API(GetModuleUserFromUserName); 1516 | REDISMODULE_GET_API(ACLCheckCommandPermissions); 1517 | REDISMODULE_GET_API(ACLCheckKeyPermissions); 1518 | REDISMODULE_GET_API(ACLCheckChannelPermissions); 1519 | REDISMODULE_GET_API(ACLAddLogEntry); 1520 | REDISMODULE_GET_API(DeauthenticateAndCloseClient); 1521 | REDISMODULE_GET_API(AuthenticateClientWithACLUser); 1522 | REDISMODULE_GET_API(AuthenticateClientWithUser); 1523 | REDISMODULE_GET_API(RedactClientCommandArgument); 1524 | REDISMODULE_GET_API(GetClientCertificate); 1525 | REDISMODULE_GET_API(GetCommandKeys); 1526 | REDISMODULE_GET_API(GetCommandKeysWithFlags); 1527 | REDISMODULE_GET_API(GetCurrentCommandName); 1528 | REDISMODULE_GET_API(RegisterDefragFunc); 1529 | REDISMODULE_GET_API(DefragAlloc); 1530 | REDISMODULE_GET_API(DefragRedisModuleString); 1531 | REDISMODULE_GET_API(DefragShouldStop); 1532 | REDISMODULE_GET_API(DefragCursorSet); 1533 | REDISMODULE_GET_API(DefragCursorGet); 1534 | REDISMODULE_GET_API(GetKeyNameFromDefragCtx); 1535 | REDISMODULE_GET_API(GetDbIdFromDefragCtx); 1536 | REDISMODULE_GET_API(EventLoopAdd); 1537 | REDISMODULE_GET_API(EventLoopDel); 1538 | REDISMODULE_GET_API(EventLoopAddOneShot); 1539 | REDISMODULE_GET_API(RegisterBoolConfig); 1540 | REDISMODULE_GET_API(RegisterNumericConfig); 1541 | REDISMODULE_GET_API(RegisterStringConfig); 1542 | REDISMODULE_GET_API(RegisterEnumConfig); 1543 | REDISMODULE_GET_API(LoadConfigs); 1544 | 1545 | if (RedisModule_IsModuleNameBusy && RedisModule_IsModuleNameBusy(name)) return REDISMODULE_ERR; 1546 | RedisModule_SetModuleAttribs(ctx,name,ver,apiver); 1547 | return REDISMODULE_OK; 1548 | } 1549 | 1550 | #define RedisModule_Assert(_e) ((_e)?(void)0 : (RedisModule__Assert(#_e,__FILE__,__LINE__),exit(1))) 1551 | 1552 | #define RMAPI_FUNC_SUPPORTED(func) (func != NULL) 1553 | 1554 | #else 1555 | 1556 | /* Things only defined for the modules core, not exported to modules 1557 | * including this file. */ 1558 | #define RedisModuleString robj 1559 | 1560 | #endif /* REDISMODULE_CORE */ 1561 | #endif /* REDISMODULE_H */ 1562 | --------------------------------------------------------------------------------