├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md └── src ├── bench ├── receive.rs └── send.rs ├── codec.rs ├── connection.rs ├── lib.rs ├── main.rs ├── messages.rs ├── publisher.rs ├── subscriber.rs └── timer.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "arrayvec" 3 | version = "0.4.10" 4 | source = "registry+https://github.com/rust-lang/crates.io-index" 5 | dependencies = [ 6 | "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", 7 | ] 8 | 9 | [[package]] 10 | name = "autocfg" 11 | version = "0.1.2" 12 | source = "registry+https://github.com/rust-lang/crates.io-index" 13 | 14 | [[package]] 15 | name = "bitflags" 16 | version = "1.0.4" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | 19 | [[package]] 20 | name = "byteorder" 21 | version = "1.3.1" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | 24 | [[package]] 25 | name = "bytes" 26 | version = "0.4.12" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | dependencies = [ 29 | "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 30 | "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 31 | ] 32 | 33 | [[package]] 34 | name = "cfg-if" 35 | version = "0.1.7" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | 38 | [[package]] 39 | name = "cloudabi" 40 | version = "0.0.3" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | dependencies = [ 43 | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 44 | ] 45 | 46 | [[package]] 47 | name = "crossbeam" 48 | version = "0.7.1" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | dependencies = [ 51 | "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 52 | "crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 53 | "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", 54 | "crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", 55 | "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 56 | "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", 57 | ] 58 | 59 | [[package]] 60 | name = "crossbeam-channel" 61 | version = "0.3.8" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | dependencies = [ 64 | "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", 65 | "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", 66 | ] 67 | 68 | [[package]] 69 | name = "crossbeam-deque" 70 | version = "0.7.1" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | dependencies = [ 73 | "crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", 74 | "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", 75 | ] 76 | 77 | [[package]] 78 | name = "crossbeam-epoch" 79 | version = "0.7.1" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | dependencies = [ 82 | "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", 83 | "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 84 | "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", 85 | "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 86 | "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 87 | "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 88 | ] 89 | 90 | [[package]] 91 | name = "crossbeam-queue" 92 | version = "0.1.2" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | dependencies = [ 95 | "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", 96 | ] 97 | 98 | [[package]] 99 | name = "crossbeam-utils" 100 | version = "0.6.5" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | dependencies = [ 103 | "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 104 | "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 105 | ] 106 | 107 | [[package]] 108 | name = "fuchsia-cprng" 109 | version = "0.1.1" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | 112 | [[package]] 113 | name = "fuchsia-zircon" 114 | version = "0.3.3" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | dependencies = [ 117 | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 118 | "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 119 | ] 120 | 121 | [[package]] 122 | name = "fuchsia-zircon-sys" 123 | version = "0.3.3" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | 126 | [[package]] 127 | name = "iovec" 128 | version = "0.1.2" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | dependencies = [ 131 | "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", 132 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 133 | ] 134 | 135 | [[package]] 136 | name = "itoa" 137 | version = "0.4.3" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | 140 | [[package]] 141 | name = "kernel32-sys" 142 | version = "0.2.2" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | dependencies = [ 145 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 146 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 147 | ] 148 | 149 | [[package]] 150 | name = "lazy_static" 151 | version = "1.3.0" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | 154 | [[package]] 155 | name = "lazycell" 156 | version = "1.2.1" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | 159 | [[package]] 160 | name = "libc" 161 | version = "0.2.51" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | 164 | [[package]] 165 | name = "lock_api" 166 | version = "0.1.5" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | dependencies = [ 169 | "owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 170 | "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 171 | ] 172 | 173 | [[package]] 174 | name = "log" 175 | version = "0.4.6" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | dependencies = [ 178 | "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 179 | ] 180 | 181 | [[package]] 182 | name = "memoffset" 183 | version = "0.2.1" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | 186 | [[package]] 187 | name = "mio" 188 | version = "0.6.16" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | dependencies = [ 191 | "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 192 | "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 193 | "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 194 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 195 | "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 196 | "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", 197 | "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 198 | "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 199 | "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 200 | "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 201 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 202 | ] 203 | 204 | [[package]] 205 | name = "mio-uds" 206 | version = "0.6.7" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | dependencies = [ 209 | "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 210 | "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", 211 | "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", 212 | ] 213 | 214 | [[package]] 215 | name = "miow" 216 | version = "0.2.1" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | dependencies = [ 219 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 220 | "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 221 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 222 | "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 223 | ] 224 | 225 | [[package]] 226 | name = "net2" 227 | version = "0.2.33" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | dependencies = [ 230 | "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 231 | "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", 232 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 233 | ] 234 | 235 | [[package]] 236 | name = "nodrop" 237 | version = "0.1.13" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | 240 | [[package]] 241 | name = "owning_ref" 242 | version = "0.4.0" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | dependencies = [ 245 | "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 246 | ] 247 | 248 | [[package]] 249 | name = "parking_lot" 250 | version = "0.7.1" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | dependencies = [ 253 | "lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 254 | "parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 255 | ] 256 | 257 | [[package]] 258 | name = "parking_lot_core" 259 | version = "0.4.0" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | dependencies = [ 262 | "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", 263 | "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", 264 | "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 265 | "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", 266 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 267 | ] 268 | 269 | [[package]] 270 | name = "proc-macro2" 271 | version = "0.4.27" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | dependencies = [ 274 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 275 | ] 276 | 277 | [[package]] 278 | name = "pubsub" 279 | version = "0.1.0" 280 | dependencies = [ 281 | "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", 282 | "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", 283 | "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", 284 | "sonr 0.1.0 (git+https://github.com/hagsteel/sonr)", 285 | ] 286 | 287 | [[package]] 288 | name = "quote" 289 | version = "0.6.12" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | dependencies = [ 292 | "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", 293 | ] 294 | 295 | [[package]] 296 | name = "rand" 297 | version = "0.6.5" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | dependencies = [ 300 | "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 301 | "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", 302 | "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 303 | "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 304 | "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 305 | "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 306 | "rand_jitter 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 307 | "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 308 | "rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 309 | "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 310 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 311 | ] 312 | 313 | [[package]] 314 | name = "rand_chacha" 315 | version = "0.1.1" 316 | source = "registry+https://github.com/rust-lang/crates.io-index" 317 | dependencies = [ 318 | "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 319 | "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 320 | ] 321 | 322 | [[package]] 323 | name = "rand_core" 324 | version = "0.3.1" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | dependencies = [ 327 | "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 328 | ] 329 | 330 | [[package]] 331 | name = "rand_core" 332 | version = "0.4.0" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | 335 | [[package]] 336 | name = "rand_hc" 337 | version = "0.1.0" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | dependencies = [ 340 | "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 341 | ] 342 | 343 | [[package]] 344 | name = "rand_isaac" 345 | version = "0.1.1" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | dependencies = [ 348 | "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 349 | ] 350 | 351 | [[package]] 352 | name = "rand_jitter" 353 | version = "0.1.3" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | dependencies = [ 356 | "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", 357 | "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 358 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 359 | ] 360 | 361 | [[package]] 362 | name = "rand_os" 363 | version = "0.1.3" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | dependencies = [ 366 | "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 367 | "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 368 | "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", 369 | "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 370 | "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 371 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 372 | ] 373 | 374 | [[package]] 375 | name = "rand_pcg" 376 | version = "0.1.2" 377 | source = "registry+https://github.com/rust-lang/crates.io-index" 378 | dependencies = [ 379 | "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 380 | "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 381 | ] 382 | 383 | [[package]] 384 | name = "rand_xorshift" 385 | version = "0.1.1" 386 | source = "registry+https://github.com/rust-lang/crates.io-index" 387 | dependencies = [ 388 | "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 389 | ] 390 | 391 | [[package]] 392 | name = "rdrand" 393 | version = "0.4.0" 394 | source = "registry+https://github.com/rust-lang/crates.io-index" 395 | dependencies = [ 396 | "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 397 | ] 398 | 399 | [[package]] 400 | name = "rustc_version" 401 | version = "0.2.3" 402 | source = "registry+https://github.com/rust-lang/crates.io-index" 403 | dependencies = [ 404 | "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 405 | ] 406 | 407 | [[package]] 408 | name = "ryu" 409 | version = "0.2.7" 410 | source = "registry+https://github.com/rust-lang/crates.io-index" 411 | 412 | [[package]] 413 | name = "scopeguard" 414 | version = "0.3.3" 415 | source = "registry+https://github.com/rust-lang/crates.io-index" 416 | 417 | [[package]] 418 | name = "semver" 419 | version = "0.9.0" 420 | source = "registry+https://github.com/rust-lang/crates.io-index" 421 | dependencies = [ 422 | "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 423 | ] 424 | 425 | [[package]] 426 | name = "semver-parser" 427 | version = "0.7.0" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | 430 | [[package]] 431 | name = "serde" 432 | version = "1.0.90" 433 | source = "registry+https://github.com/rust-lang/crates.io-index" 434 | dependencies = [ 435 | "serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", 436 | ] 437 | 438 | [[package]] 439 | name = "serde_derive" 440 | version = "1.0.90" 441 | source = "registry+https://github.com/rust-lang/crates.io-index" 442 | dependencies = [ 443 | "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", 444 | "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", 445 | "syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)", 446 | ] 447 | 448 | [[package]] 449 | name = "serde_json" 450 | version = "1.0.39" 451 | source = "registry+https://github.com/rust-lang/crates.io-index" 452 | dependencies = [ 453 | "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 454 | "ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", 455 | "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", 456 | ] 457 | 458 | [[package]] 459 | name = "slab" 460 | version = "0.4.2" 461 | source = "registry+https://github.com/rust-lang/crates.io-index" 462 | 463 | [[package]] 464 | name = "smallvec" 465 | version = "0.6.9" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | 468 | [[package]] 469 | name = "sonr" 470 | version = "0.1.0" 471 | source = "git+https://github.com/hagsteel/sonr#a668c34bb822a95a0bd4564e20f489063f546a36" 472 | dependencies = [ 473 | "crossbeam 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", 474 | "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", 475 | "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", 476 | "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", 477 | ] 478 | 479 | [[package]] 480 | name = "stable_deref_trait" 481 | version = "1.1.1" 482 | source = "registry+https://github.com/rust-lang/crates.io-index" 483 | 484 | [[package]] 485 | name = "syn" 486 | version = "0.15.30" 487 | source = "registry+https://github.com/rust-lang/crates.io-index" 488 | dependencies = [ 489 | "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", 490 | "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", 491 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 492 | ] 493 | 494 | [[package]] 495 | name = "unicode-xid" 496 | version = "0.1.0" 497 | source = "registry+https://github.com/rust-lang/crates.io-index" 498 | 499 | [[package]] 500 | name = "winapi" 501 | version = "0.2.8" 502 | source = "registry+https://github.com/rust-lang/crates.io-index" 503 | 504 | [[package]] 505 | name = "winapi" 506 | version = "0.3.7" 507 | source = "registry+https://github.com/rust-lang/crates.io-index" 508 | dependencies = [ 509 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 510 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 511 | ] 512 | 513 | [[package]] 514 | name = "winapi-build" 515 | version = "0.1.1" 516 | source = "registry+https://github.com/rust-lang/crates.io-index" 517 | 518 | [[package]] 519 | name = "winapi-i686-pc-windows-gnu" 520 | version = "0.4.0" 521 | source = "registry+https://github.com/rust-lang/crates.io-index" 522 | 523 | [[package]] 524 | name = "winapi-x86_64-pc-windows-gnu" 525 | version = "0.4.0" 526 | source = "registry+https://github.com/rust-lang/crates.io-index" 527 | 528 | [[package]] 529 | name = "ws2_32-sys" 530 | version = "0.2.1" 531 | source = "registry+https://github.com/rust-lang/crates.io-index" 532 | dependencies = [ 533 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 534 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 535 | ] 536 | 537 | [metadata] 538 | "checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" 539 | "checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799" 540 | "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" 541 | "checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" 542 | "checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" 543 | "checksum cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4" 544 | "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" 545 | "checksum crossbeam 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b14492071ca110999a20bf90e3833406d5d66bfd93b4e52ec9539025ff43fe0d" 546 | "checksum crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f0ed1a4de2235cabda8558ff5840bffb97fcb64c97827f354a451307df5f72b" 547 | "checksum crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" 548 | "checksum crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "04c9e3102cc2d69cd681412141b390abd55a362afc1540965dad0ad4d34280b4" 549 | "checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" 550 | "checksum crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f8306fcef4a7b563b76b7dd949ca48f52bc1141aa067d2ea09565f3e2652aa5c" 551 | "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" 552 | "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" 553 | "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" 554 | "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" 555 | "checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" 556 | "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 557 | "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" 558 | "checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" 559 | "checksum libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "bedcc7a809076656486ffe045abeeac163da1b558e963a31e29fbfbeba916917" 560 | "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" 561 | "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" 562 | "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" 563 | "checksum mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "71646331f2619b1026cc302f87a2b8b648d5c6dd6937846a16cc8ce0f347f432" 564 | "checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" 565 | "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" 566 | "checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" 567 | "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" 568 | "checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13" 569 | "checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" 570 | "checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" 571 | "checksum proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)" = "4d317f9caece796be1980837fd5cb3dfec5613ebdb04ad0956deea83ce168915" 572 | "checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db" 573 | "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" 574 | "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" 575 | "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" 576 | "checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0" 577 | "checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" 578 | "checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" 579 | "checksum rand_jitter 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b9ea758282efe12823e0d952ddb269d2e1897227e464919a554f2a03ef1b832" 580 | "checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" 581 | "checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" 582 | "checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" 583 | "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" 584 | "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 585 | "checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7" 586 | "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" 587 | "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 588 | "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 589 | "checksum serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)" = "aa5f7c20820475babd2c077c3ab5f8c77a31c15e16ea38687b4c02d3e48680f4" 590 | "checksum serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)" = "58fc82bec244f168b23d1963b45c8bf5726e9a15a9d146a067f9081aeed2de79" 591 | "checksum serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "5a23aa71d4a4d43fdbfaac00eff68ba8a06a51759a89ac3304323e800c4dd40d" 592 | "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" 593 | "checksum smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c4488ae950c49d403731982257768f48fada354a5203fe81f9bb6f43ca9002be" 594 | "checksum sonr 0.1.0 (git+https://github.com/hagsteel/sonr)" = "" 595 | "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" 596 | "checksum syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)" = "66c8865bf5a7cbb662d8b011950060b3c8743dca141b054bf7195b20d314d8e2" 597 | "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 598 | "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 599 | "checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" 600 | "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 601 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 602 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 603 | "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" 604 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pubsub" 3 | version = "0.1.0" 4 | authors = ["Jonas Hagstedt "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | sonr = { git = "https://github.com/hagsteel/sonr" } 9 | serde = { version = "1.0.90", features = ["derive"] } 10 | bytes = "0.4.12" 11 | serde_json = "1.0.39" 12 | 13 | [profile.release] 14 | debug = false 15 | lto = true 16 | panic = 'abort' 17 | 18 | [[bin]] 19 | name = "send" 20 | path = "src/bench/send.rs" 21 | 22 | [[bin]] 23 | name = "receive" 24 | path = "src/bench/receive.rs" 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Jonas 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 | # Pub/Sub example using Sonr 2 | 3 | See blog post for context: https://hagsteel.com/posts/building-a-pub-sub-with-sonr-part-1/ 4 | -------------------------------------------------------------------------------- /src/bench/receive.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | use std::collections::HashMap; 3 | use std::thread; 4 | use std::sync::atomic::{AtomicUsize, Ordering}; 5 | use std::net::SocketAddr; 6 | 7 | use sonr::errors::Result; 8 | use sonr::net::tcp::{ReactiveTcpStream, TcpStream}; 9 | use sonr::prelude::*; 10 | 11 | use pubsub::connection::Connection; 12 | use pubsub::messages::{Subscribe, PubMessage}; 13 | use pubsub::codec::LineCodec; 14 | 15 | static COUNTER: AtomicUsize = AtomicUsize::new(0); 16 | 17 | struct Connections { 18 | connections: HashMap, 19 | } 20 | 21 | impl Connections { 22 | pub fn new(con_count: usize) -> Self { 23 | let addr: SocketAddr = "127.0.0.1:9000".parse().unwrap(); 24 | let connections = (0..con_count).map(|_| { 25 | let stream = TcpStream::connect(&addr).unwrap(); 26 | let stream = ReactiveTcpStream::new(stream).unwrap(); 27 | let token = stream.token(); 28 | let mut con = Connection::new(stream); 29 | let payload = LineCodec::encode(Subscribe { channel: "abc".to_owned() }).unwrap(); 30 | con.add_payload(payload); 31 | (token, con) 32 | }).collect::>(); 33 | 34 | Self { 35 | connections, 36 | } 37 | } 38 | } 39 | 40 | impl Reactor for Connections { 41 | type Input = (); 42 | type Output = (); 43 | 44 | fn react(&mut self, reaction: Reaction) -> Reaction { 45 | use Reaction::*; 46 | match reaction { 47 | Event(event) => { 48 | let connection_id = event.token(); 49 | if let Some(connection) = self.connections.get_mut(&connection_id) { 50 | connection.react(event.into()); 51 | 52 | while let Some(messages) = connection.recv::() { 53 | match messages { 54 | Ok(msg) => { 55 | COUNTER.fetch_add(msg.len(), Ordering::SeqCst); 56 | } 57 | Err(_) => { 58 | self.connections.remove(&connection_id); 59 | return Continue 60 | } 61 | } 62 | } 63 | 64 | while let Some(res) = connection.write() { 65 | if res.is_err() { 66 | self.connections.remove(&connection_id); 67 | return Continue; 68 | } 69 | } 70 | 71 | Continue 72 | } else { 73 | event.into() 74 | } 75 | } 76 | Value(()) => Continue, 77 | Continue => Continue 78 | } 79 | } 80 | } 81 | 82 | fn main() { 83 | let thread_count = 8; 84 | let con_per_thread = 1; 85 | let mut handles = Vec::new(); 86 | 87 | for _ in 0..thread_count { 88 | let handle = thread::spawn(move || -> Result<()> { 89 | System::init()?; 90 | let subscribing = Connections::new(con_per_thread); 91 | let run = subscribing; 92 | System::start(run)?; 93 | Ok(()) 94 | }); 95 | 96 | handles.push(handle); 97 | } 98 | 99 | let mul = 8usize; 100 | thread::sleep(Duration::from_secs(mul as u64)); 101 | 102 | let count = COUNTER.load(Ordering::SeqCst); 103 | let msg_per_sec = count / mul; 104 | eprintln!("{} msg/s", msg_per_sec); 105 | eprintln!("{} messages", count); 106 | } 107 | -------------------------------------------------------------------------------- /src/bench/send.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | use std::collections::HashMap; 3 | use std::thread; 4 | use std::sync::atomic::{AtomicUsize, Ordering}; 5 | use std::net::SocketAddr; 6 | use bytes::{Bytes, BytesMut, BufMut}; 7 | 8 | use sonr::errors::Result; 9 | use sonr::net::tcp::{ReactiveTcpStream, TcpStream}; 10 | use sonr::prelude::*; 11 | 12 | use pubsub::connection::Connection; 13 | use pubsub::messages::AckMessage; 14 | 15 | static COUNTER: AtomicUsize = AtomicUsize::new(0); 16 | static FAIL_COUNTER: AtomicUsize = AtomicUsize::new(0); 17 | const SEND_TRIGGER: usize = 256; 18 | static PAYLOAD: &'static [u8] = b"{\"channel\":\"abc\",\"payload\":\"hello\"}\n"; 19 | 20 | fn payload(count: usize) -> Bytes { 21 | let mut b = BytesMut::with_capacity(count * PAYLOAD.len()); 22 | (0..count).for_each(|_| { 23 | b.put_slice(PAYLOAD) 24 | }); 25 | 26 | b.freeze() 27 | } 28 | 29 | struct Connections { 30 | connections: HashMap, 31 | } 32 | 33 | impl Connections { 34 | pub fn new(con_count: usize) -> Self { 35 | let addr: SocketAddr = "127.0.0.1:8000".parse().unwrap(); 36 | let connections = (0..con_count).map(|_| { 37 | let stream = TcpStream::connect(&addr).unwrap(); 38 | let stream = ReactiveTcpStream::new(stream).unwrap(); 39 | let token = stream.token(); 40 | let mut con = Connection::new(stream); 41 | con.add_payload(payload(SEND_TRIGGER)); 42 | (token, con) 43 | }).collect::>(); 44 | 45 | Self { 46 | connections, 47 | } 48 | } 49 | } 50 | 51 | impl Reactor for Connections { 52 | type Input = (); 53 | type Output = (); 54 | 55 | fn react(&mut self, reaction: Reaction) -> Reaction { 56 | use Reaction::*; 57 | match reaction { 58 | Event(event) => { 59 | let connection_id = event.token(); 60 | if let Some(connection) = self.connections.get_mut(&connection_id) { 61 | connection.react(event.into()); 62 | 63 | let mut ok_msg_count = 0usize; 64 | while let Some(messages) = connection.recv::() { 65 | match messages { 66 | Ok(msg) => { 67 | COUNTER.fetch_add(msg.len(), Ordering::SeqCst); 68 | ok_msg_count += msg.len(); 69 | } 70 | Err(_) => { 71 | self.connections.remove(&connection_id); 72 | return Continue 73 | } 74 | } 75 | } 76 | 77 | connection.add_payload(payload(ok_msg_count)); 78 | 79 | while let Some(res) = connection.write() { 80 | if res.is_err() { 81 | self.connections.remove(&connection_id); 82 | return Continue; 83 | } 84 | } 85 | 86 | Continue 87 | } else { 88 | event.into() 89 | } 90 | } 91 | Value(()) => Continue, 92 | Continue => Continue 93 | } 94 | } 95 | } 96 | 97 | fn main() { 98 | let thread_count = 8; 99 | let con_per_thread = 1; 100 | let mut handles = Vec::new(); 101 | 102 | for _ in 0..thread_count { 103 | let handle = thread::spawn(move || -> Result<()> { 104 | System::init()?; 105 | let subscribing = Connections::new(con_per_thread); 106 | let run = subscribing; 107 | System::start(run)?; 108 | Ok(()) 109 | }); 110 | 111 | handles.push(handle); 112 | } 113 | 114 | let mul = 8usize; 115 | thread::sleep(Duration::from_secs(mul as u64)); 116 | 117 | let count = COUNTER.load(Ordering::SeqCst); 118 | let msg_per_sec = count / mul; 119 | let fail_count = FAIL_COUNTER.load(Ordering::SeqCst); 120 | let payload = payload(1); 121 | let mb = (count * payload.len()) / 1024 / 1024; 122 | eprintln!("{} msg/s", msg_per_sec); 123 | eprintln!("{} messages", count); 124 | eprintln!("{} messages failed", fail_count); 125 | eprintln!("{} MB/s", mb / mul); 126 | } 127 | -------------------------------------------------------------------------------- /src/codec.rs: -------------------------------------------------------------------------------- 1 | use bytes::{Bytes, BytesMut}; 2 | use serde::de::DeserializeOwned; 3 | use serde::Serialize; 4 | use serde_json::error::Result; 5 | 6 | use crate::BUFFER_SIZE; 7 | 8 | pub struct LineCodec; 9 | 10 | impl LineCodec { 11 | pub fn decode(buf: &mut BytesMut) -> Option { 12 | let p = buf.iter().position(|b| b == &b'\n'); 13 | match p { 14 | None => None, 15 | Some(n) => { 16 | let res = serde_json::from_slice(&buf.split_to(n).freeze()).ok(); 17 | buf.advance(1); // Skip the newline char 18 | buf.reserve(BUFFER_SIZE); // Make sure the buffer can hold more data 19 | res 20 | } 21 | } 22 | } 23 | 24 | pub fn encode(t: T) -> Result { 25 | let mut payload = serde_json::to_vec(&t)?; 26 | payload.push(b'\n'); 27 | Ok(Bytes::from(payload)) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/connection.rs: -------------------------------------------------------------------------------- 1 | use bytes::{Bytes, BufMut, BytesMut}; 2 | use serde::de::DeserializeOwned; 3 | use sonr::net::tcp::ReactiveTcpStream; 4 | use sonr::reactor::{Reaction, Reactor}; 5 | use std::io::{ErrorKind::WouldBlock, Read, Write}; 6 | 7 | use crate::codec::LineCodec; 8 | use crate::BUFFER_SIZE; 9 | 10 | pub struct Connection { 11 | stream: ReactiveTcpStream, 12 | read_buffer: BytesMut, 13 | write_buffer: BytesMut, 14 | } 15 | 16 | impl Connection { 17 | pub fn new(stream: ReactiveTcpStream) -> Self { 18 | Self { 19 | stream, 20 | read_buffer: BytesMut::with_capacity(BUFFER_SIZE), 21 | write_buffer: BytesMut::with_capacity(BUFFER_SIZE), 22 | } 23 | } 24 | 25 | pub fn recv(&mut self) -> Option, ()>> { 26 | if !self.stream.readable() { 27 | return None; 28 | } 29 | 30 | let res = { 31 | let mut b = unsafe { self.read_buffer.bytes_mut() }; 32 | self.stream.read(&mut b) 33 | }; 34 | 35 | match res { 36 | // The connection was closed by the peer. 37 | Ok(0) => Some(Err(())), 38 | 39 | // Try to decode messages from the read data 40 | Ok(n) => { 41 | let buf_len = self.read_buffer.len() + n; 42 | unsafe { self.read_buffer.set_len(buf_len); } 43 | 44 | let mut v = Vec::new(); 45 | while let Some(val) = LineCodec::decode(&mut self.read_buffer) { 46 | v.push(val); 47 | } 48 | 49 | Some(Ok(v)) 50 | } 51 | 52 | // Not an actual error 53 | Err(ref e) if e.kind() == WouldBlock => None, 54 | 55 | // Connection closed. Ignoring the reason 56 | // for simplicity 57 | Err(_) => Some(Err(())), 58 | } 59 | } 60 | 61 | pub fn write(&mut self) -> Option> { 62 | if !self.stream.writable() { 63 | return None 64 | } 65 | 66 | if self.write_buffer.is_empty() { 67 | return None 68 | } 69 | 70 | match self.stream.write(&self.write_buffer) { 71 | Ok(n) => { 72 | self.write_buffer.split_to(n); // Remove sent data 73 | Some(Ok(n)) 74 | } 75 | Err(ref e) if e.kind() == WouldBlock => None, 76 | Err(_) => Some(Err(())), 77 | } 78 | } 79 | 80 | pub fn add_payload(&mut self, payload: Bytes) { 81 | if payload.len() > self.write_buffer.remaining_mut() { 82 | self.write_buffer.reserve(payload.len()); 83 | } 84 | self.write_buffer.put_slice(&payload); 85 | } 86 | 87 | // Convenience, saving us from having to make the stream public 88 | pub fn react(&mut self, reaction: Reaction<()>) -> Reaction<()> { 89 | self.stream.react(reaction) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod codec; 2 | pub mod connection; 3 | pub mod messages; 4 | pub mod publisher; 5 | pub mod subscriber; 6 | pub mod timer; 7 | 8 | const BUFFER_SIZE: usize = 1024 * 8; 9 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::thread; 2 | use std::time::Duration; 3 | use sonr::prelude::*; 4 | use sonr::errors::Result; 5 | use sonr::net::tcp::{TcpStream, ReactiveTcpListener}; 6 | use sonr::sync::broadcast::Broadcast; 7 | use sonr::sync::queue::{ReactiveQueue, ReactiveDeque}; 8 | 9 | use pubsub::publisher::Publisher; 10 | use pubsub::subscriber::Subscriber; 11 | use pubsub::timer::Timer; 12 | 13 | fn listener(addr: &str) -> Result> { 14 | let l = ReactiveTcpListener::bind(addr)? 15 | .map(|(s, _)| s); 16 | Ok(l) 17 | } 18 | 19 | 20 | fn main() -> Result<()> { 21 | System::init()?; 22 | let thread_count = 8; 23 | let buffer_threshold = 256; 24 | let publish_timeout = Duration::from_millis(20); 25 | 26 | // Publisher 27 | let pub_listener = listener("127.0.0.1:8000")?; 28 | let broadcast = Broadcast::unbounded(); 29 | let mut timer = Timer::new(publish_timeout); 30 | let mut pub_connection_queue = ReactiveQueue::unbounded(); 31 | 32 | // Subscriber 33 | let sub_listener = listener("127.0.0.1:9000")?; 34 | let mut sub_connection_queue = ReactiveQueue::unbounded(); 35 | 36 | for _ in 0..thread_count { 37 | let broadcast = broadcast.clone(); 38 | let timer_notifier = timer.receiver(); 39 | let pub_deque = pub_connection_queue.deque(); 40 | let sub_deque = sub_connection_queue.deque(); 41 | 42 | thread::spawn(move || -> Result<()> { 43 | System::init()?; 44 | 45 | let sub_connection_deque = ReactiveDeque::new(sub_deque)?; 46 | let subscriber = Subscriber::new(broadcast.subscriber())?; 47 | let sub_run = sub_connection_deque.chain(subscriber); 48 | 49 | let pub_connection_deque = ReactiveDeque::new(pub_deque)?; 50 | let publisher = Publisher::new(broadcast, buffer_threshold, timer_notifier)?; 51 | let pub_run = pub_connection_deque.chain(publisher); 52 | 53 | let run = pub_run.and(sub_run); 54 | 55 | System::start(run)?; 56 | 57 | Ok(()) 58 | }); 59 | } 60 | 61 | timer.start(); 62 | 63 | let pub_run = pub_listener.chain(pub_connection_queue); 64 | let sub_run = sub_listener.chain(sub_connection_queue); 65 | 66 | System::start(pub_run.and(sub_run))?; 67 | 68 | Ok(()) 69 | } 70 | -------------------------------------------------------------------------------- /src/messages.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Deserialize, Serialize, Debug, Clone)] 4 | pub struct PubMessage { 5 | pub channel: String, 6 | pub payload: String, 7 | } 8 | 9 | #[derive(Deserialize, Serialize, Debug, Clone)] 10 | pub struct AckMessage { 11 | ack: bool, 12 | } 13 | 14 | impl AckMessage { 15 | pub fn new() -> Self { 16 | Self { ack: true } 17 | } 18 | } 19 | 20 | #[derive(Deserialize, Serialize, Debug, Clone)] 21 | pub struct Subscribe { 22 | pub channel: String, 23 | } 24 | -------------------------------------------------------------------------------- /src/publisher.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use sonr::reactor::{Reactor, Reaction}; 4 | use sonr::sync::broadcast::Broadcast; 5 | use sonr::net::tcp::{ReactiveTcpStream, TcpStream}; 6 | use sonr::Token; 7 | use sonr::errors::Result; 8 | use bytes::{Bytes, BytesMut, BufMut}; 9 | 10 | use crate::connection::Connection; 11 | use crate::codec::LineCodec; 12 | use crate::messages::{PubMessage, AckMessage}; 13 | use crate::timer::{TimerNotifier, ReactiveTimerNotifier}; 14 | 15 | 16 | pub struct Publisher { 17 | connections: HashMap, 18 | broadcast: Broadcast, 19 | buffer_threshold: usize, // buffer messages 20 | publish_payload: BytesMut, 21 | timer: ReactiveTimerNotifier, 22 | } 23 | 24 | impl Publisher { 25 | pub fn new(broadcast: Broadcast, buffer_threshold: usize, timer: TimerNotifier) -> Result { 26 | let timer = ReactiveTimerNotifier::new(timer)?; 27 | 28 | Ok(Self { 29 | connections: HashMap::new(), 30 | broadcast, 31 | buffer_threshold, 32 | publish_payload: BytesMut::with_capacity(buffer_threshold * 2), 33 | timer, 34 | }) 35 | } 36 | } 37 | 38 | impl Reactor for Publisher { 39 | type Input = TcpStream; 40 | type Output = (); 41 | 42 | fn react(&mut self, reaction: Reaction) -> Reaction { 43 | use Reaction::*; 44 | match reaction { 45 | Value(stream) => { 46 | if let Ok(stream) = ReactiveTcpStream::new(stream) { 47 | self.connections.insert(stream.token(), Connection::new(stream)); 48 | } 49 | Continue 50 | } 51 | 52 | Event(event) => { 53 | // Timer tick event: 54 | if event.token() == self.timer.token() { 55 | // We can ignore the result as it's simply a unit, 56 | // however we should get the result out to make room 57 | // for the next one. 58 | let _ = self.timer.try_recv(); 59 | 60 | // Only publish if we have actual data 61 | if !self.publish_payload.is_empty() { 62 | self.broadcast.publish(self.publish_payload.take().freeze()); 63 | } 64 | } 65 | 66 | // Connection event: 67 | if let Some(con) = self.connections.get_mut(&event.token()) { 68 | con.react(event.into()); // Mark the underlying stream as readable / writable 69 | 70 | // Read messages to publish. 71 | // Note: could simply send an "ack" message for every "\n" 72 | // char, however this ensures that the message is an actual `PubMessage` 73 | while let Some(msg_result) = con.recv::() { 74 | match msg_result { 75 | Ok(messages) => { 76 | for message in messages { 77 | if let Ok(bytes) = LineCodec::encode(&message) { 78 | if bytes.len() > self.publish_payload.remaining_mut() { 79 | self.publish_payload.reserve(self.buffer_threshold); 80 | } 81 | self.publish_payload.put_slice(&bytes); 82 | } 83 | 84 | // ack message 85 | let _ = LineCodec::encode(AckMessage::new()).map(|payload| con.add_payload(payload)); 86 | } 87 | } 88 | Err(_) => { 89 | self.connections.remove(&event.token()); 90 | 91 | // Publish the payload 92 | self.broadcast.publish(self.publish_payload.take().freeze()); 93 | return Continue 94 | } 95 | } 96 | } 97 | 98 | // If enough data is buffered then publish the messages. 99 | if self.publish_payload.len() >= self.buffer_threshold { 100 | self.broadcast.publish(self.publish_payload.take().freeze()); 101 | } 102 | 103 | // Write all ack messages 104 | while let Some(wrt_res) = con.write() { 105 | if wrt_res.is_err() { 106 | self.connections.remove(&event.token()); 107 | return Continue 108 | } 109 | } 110 | 111 | Continue 112 | } else { 113 | event.into() 114 | } 115 | } 116 | 117 | Continue => Continue, 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/subscriber.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use sonr::Token; 4 | use sonr::reactor::{Reactor, Reaction}; 5 | use sonr::errors::Result; 6 | use sonr::net::tcp::{TcpStream, ReactiveTcpStream}; 7 | use sonr::sync::signal::{SignalReceiver, ReactiveSignalReceiver}; 8 | use bytes::{Bytes, BytesMut, BufMut}; 9 | 10 | use crate::connection::Connection; 11 | use crate::codec::LineCodec; 12 | use crate::messages::{PubMessage, Subscribe}; 13 | use crate::BUFFER_SIZE; 14 | 15 | pub struct Subscriber { 16 | connections: HashMap, 17 | messages: ReactiveSignalReceiver, 18 | channels: HashMap>, 19 | message_buffer: BytesMut, 20 | } 21 | 22 | impl Subscriber { 23 | pub fn new(messages: SignalReceiver) -> Result { 24 | Ok(Self { 25 | connections: HashMap::new(), 26 | messages: ReactiveSignalReceiver::new(messages)?, 27 | channels: HashMap::new(), 28 | message_buffer: BytesMut::with_capacity(BUFFER_SIZE), 29 | }) 30 | } 31 | 32 | fn publish(&mut self) { 33 | loop { 34 | let message = LineCodec::decode::(&mut self.message_buffer); 35 | if message.is_none() { return } 36 | 37 | let message = message.unwrap(); 38 | if let Some(connection_ids) = self.channels.get(&message.channel) { 39 | let connection_ids = connection_ids.clone(); 40 | 41 | if let Ok(encoded_message) = LineCodec::encode(message) { 42 | for cid in connection_ids { 43 | if let Some(con) = self.connections.get_mut(&cid) { 44 | con.add_payload(encoded_message.clone()); 45 | 46 | while let Some(wrt_res) = con.write() { 47 | if wrt_res.is_err() { 48 | self.connections.remove(&cid); 49 | self.unsubscribe(cid); 50 | break; 51 | } 52 | } 53 | } 54 | } 55 | } 56 | } 57 | } 58 | } 59 | 60 | fn unsubscribe(&mut self, connection_id: Token) { 61 | for connection_ids in self.channels.values_mut() { 62 | while let Some(pos) = connection_ids.iter().position(|id| id == &connection_id) { 63 | connection_ids.remove(pos); 64 | } 65 | } 66 | } 67 | 68 | fn add_payload(&mut self, payload: Bytes) { 69 | if payload.len() > self.message_buffer.remaining_mut() { 70 | self.message_buffer.reserve(payload.len()); 71 | } 72 | self.message_buffer.put_slice(&payload); 73 | } 74 | } 75 | 76 | impl Reactor for Subscriber { 77 | type Input = TcpStream; 78 | type Output = (); 79 | 80 | fn react(&mut self, reaction: Reaction) -> Reaction { 81 | use Reaction::*; 82 | match reaction { 83 | Value(stream) => { 84 | if let Ok(stream) = ReactiveTcpStream::new(stream) { 85 | self.connections.insert(stream.token(), Connection::new(stream)); 86 | } 87 | Continue 88 | } 89 | Event(event) => { 90 | // Incoming messages: 91 | if self.messages.token() == event.token() { 92 | if let Value(messages) = self.messages.react(event.into()) { 93 | self.add_payload(messages); 94 | 95 | // Keep "reacting" until we no longer receive a message 96 | while let Value(messages) = self.messages.react(Continue) { 97 | self.add_payload(messages); 98 | } 99 | 100 | self.publish(); 101 | } 102 | return Continue 103 | } 104 | 105 | // Connection event: 106 | if let Some(con) = self.connections.get_mut(&event.token()) { 107 | con.react(event.into()); 108 | 109 | // Read all "subscribe" messages 110 | while let Some(messages) = con.recv::() { 111 | match messages { 112 | Ok(messages) => { 113 | for message in messages { 114 | match self.channels.get_mut(&message.channel) { 115 | Some(_) => { 116 | if let Some(conneciton_ids) = self.channels.get_mut(&message.channel) { 117 | conneciton_ids.push(event.token()); 118 | } 119 | } 120 | None => { self.channels.insert(message.channel, vec![event.token()]); } 121 | } 122 | } 123 | } 124 | Err(_) => { 125 | self.connections.remove(&event.token()); 126 | self.unsubscribe(event.token()); 127 | return Continue 128 | } 129 | } 130 | } 131 | Continue 132 | } else { 133 | event.into() 134 | } 135 | } 136 | Continue => Continue, 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/timer.rs: -------------------------------------------------------------------------------- 1 | use sonr::sync::signal::{SignalSender, SignalReceiver, ReactiveSignalReceiver}; 2 | use std::thread; 3 | use std::time::Duration; 4 | 5 | pub type ReactiveTimerNotifier = ReactiveSignalReceiver<()>; 6 | pub type TimerNotifier = SignalReceiver<()>; 7 | 8 | pub struct Timer { 9 | senders: Vec>, 10 | interval: Duration, 11 | } 12 | 13 | impl Timer { 14 | pub fn new(interval: Duration) -> Self { 15 | Self { 16 | senders: Vec::new(), 17 | interval, 18 | } 19 | } 20 | 21 | pub fn receiver(&mut self) -> SignalReceiver<()> { 22 | let receiver = SignalReceiver::bounded(1); 23 | self.senders.push(receiver.sender()); 24 | receiver 25 | } 26 | 27 | pub fn start(self) { 28 | thread::spawn(move || { 29 | loop { 30 | thread::sleep(self.interval); 31 | self.senders.iter().for_each(|n| { let _ = n.send(()); } ); 32 | } 33 | }); 34 | } 35 | } 36 | --------------------------------------------------------------------------------