├── .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 |
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 |
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