Skip to content

Commit

Permalink
Add authority router
Browse files Browse the repository at this point in the history
  • Loading branch information
CathalMullan committed Dec 30, 2024
1 parent 227aa6f commit 6df8ad1
Show file tree
Hide file tree
Showing 63 changed files with 7,165 additions and 2,759 deletions.
44 changes: 44 additions & 0 deletions BENCHMARKING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Benchmarking

All benchmarks ran on a M1 Pro laptop running Asahi Linux.

Check out our [codspeed results](https://codspeed.io/DuskSystems/wayfind/benchmarks) for a more accurate set of timings.

## Context

For all benchmarks, we percent-decode the path before matching.
After matching, we convert any extracted parameters to strings.

Some routers perform these operations automatically, while others require them to be done manually.

We do this to try and match behaviour as best as possible. This is as close to an "apples-to-apples" comparison as we can get.

## `matchit` inspired benches

In a router of 130 routes, benchmark matching 4 paths.

| Library | Time | Alloc Count | Alloc Size | Dealloc Count | Dealloc Size |
|:-----------------|----------:|------------:|-----------:|--------------:|-------------:|
| wayfind | 465.02 ns | 5 | 329 B | 5 | 329 B |
| matchit | 551.32 ns | 5 | 480 B | 5 | 512 B |
| path-tree | 564.04 ns | 5 | 480 B | 5 | 512 B |
| xitca-router | 646.81 ns | 8 | 864 B | 8 | 896 B |
| ntex-router | 2.2001 µs | 19 | 1.312 KB | 19 | 1.344 KB |
| route-recognizer | 3.1331 µs | 161 | 8.569 KB | 161 | 8.601 KB |
| routefinder | 6.1604 µs | 68 | 5.088 KB | 68 | 5.12 KB |
| actix-router | 20.956 µs | 215 | 14 KB | 215 | 14.03 KB |

## `path-tree` inspired benches

In a router of 320 routes, benchmark matching 80 paths.

| Library | Time | Alloc Count | Alloc Size | Dealloc Count | Dealloc Size |
|:-----------------|----------:|------------:|-----------:|--------------:|-------------:|
| wayfind | 6.6879 µs | 60 | 3.847 KB | 60 | 3.847 KB |
| path-tree | 8.4434 µs | 60 | 8.727 KB | 60 | 8.75 KB |
| matchit | 9.8761 µs | 141 | 19.09 KB | 141 | 19.11 KB |
| xitca-router | 11.651 µs | 210 | 26.79 KB | 210 | 26.81 KB |
| ntex-router | 35.669 µs | 202 | 20.82 KB | 202 | 20.84 KB |
| route-recognizer | 69.671 µs | 2873 | 192.9 KB | 2873 | 206.1 KB |
| routefinder | 87.075 µs | 526 | 49.68 KB | 526 | 49.71 KB |
| actix-router | 185.31 µs | 2202 | 130.1 KB | 2202 | 130.1 KB |
20 changes: 10 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

89 changes: 24 additions & 65 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

A speedy, flexible router for Rust.

NOTE: `wayfind` is still a work in progress.
NOTE: `wayfind` is still a work in progress.

## Why another router?

Expand Down Expand Up @@ -500,6 +500,8 @@ fn main() -> Result<(), Box<dyn Error>> {
router.insert(&route, "handle_not_found")?;

insta::assert_snapshot!(router, @r"
=== Authority
Empty
=== Path
/
├─ user [9]
Expand Down Expand Up @@ -568,26 +570,26 @@ fn main() -> Result<(), Box<dyn Error>> {
├─ GET [17]
╰─ PUT [18]
=== Chains
1-1
1-2
2-3
3-4
4-5
4-6
4-7
5-8
6-9
7-10
8-11
8-12
9-13
10-14
11-15
12-16
13-17
13-18
13-19
14-*
*-1-1
*-1-2
*-2-3
*-3-4
*-4-5
*-4-6
*-4-7
*-5-8
*-6-9
*-7-10
*-8-11
*-8-12
*-9-13
*-10-14
*-11-15
*-12-16
*-13-17
*-13-18
*-13-19
*-14-*
");

Ok(())
Expand All @@ -601,50 +603,7 @@ fn main() -> Result<(), Box<dyn Error>> {
However, as is often the case, your mileage may vary (YMMV).
Benchmarks, especially micro-benchmarks, should be taken with a grain of salt.

### Benchmarks

All benchmarks ran on a M1 Pro laptop running Asahi Linux.

Check out our [codspeed results](https://codspeed.io/DuskSystems/wayfind/benchmarks) for a more accurate set of timings.

#### Context

For all benchmarks, we percent-decode the path before matching.
After matching, we convert any extracted parameters to strings.

Some routers perform these operations automatically, while others require them to be done manually.

We do this to try and match behaviour as best as possible. This is as close to an "apples-to-apples" comparison as we can get.

#### `matchit` inspired benches

In a router of 130 routes, benchmark matching 4 paths.

| Library | Time | Alloc Count | Alloc Size | Dealloc Count | Dealloc Size |
|:-----------------|----------:|------------:|-----------:|--------------:|-------------:|
| wayfind | 415.67 ns | 4 | 265 B | 4 | 265 B |
| matchit | 559.16 ns | 4 | 416 B | 4 | 448 B |
| path-tree | 570.10 ns | 4 | 416 B | 4 | 448 B |
| xitca-router | 650.12 ns | 7 | 800 B | 7 | 832 B |
| ntex-router | 2.2439 µs | 18 | 1.248 KB | 18 | 1.28 KB |
| route-recognizer | 3.1662 µs | 160 | 8.505 KB | 160 | 8.537 KB |
| routefinder | 6.2237 µs | 67 | 5.024 KB | 67 | 5.056 KB |
| actix-router | 21.072 µs | 214 | 13.93 KB | 214 | 13.96 KB |

#### `path-tree` inspired benches

In a router of 320 routes, benchmark matching 80 paths.

| Library | Time | Alloc Count | Alloc Size | Dealloc Count | Dealloc Size |
|:-----------------|----------:|------------:|-----------:|--------------:|-------------:|
| wayfind | 5.9508 µs | 59 | 2.567 KB | 59 | 2.567 KB |
| path-tree | 8.5983 µs | 59 | 7.447 KB | 59 | 7.47 KB |
| matchit | 9.8800 µs | 140 | 17.81 KB | 140 | 17.83 KB |
| xitca-router | 11.930 µs | 209 | 25.51 KB | 209 | 25.53 KB |
| ntex-router | 35.919 µs | 201 | 19.54 KB | 201 | 19.56 KB |
| route-recognizer | 69.604 µs | 2872 | 191.7 KB | 2872 | 204.8 KB |
| routefinder | 87.659 µs | 525 | 48.40 KB | 525 | 48.43 KB |
| actix-router | 187.49 µs | 2201 | 128.8 KB | 2201 | 128.8 KB |
See [BENCHMARKING.md](BENCHMARKING.md) for the results.

## License

Expand Down
10 changes: 10 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# TODO

- Authority router.
- Stop using the term 'route' to mean 'template'.
- Split routers into seperate crates.
- Dedupe the 2 tree routers? (Auth vs Patha)
- Consider removing expanded routes, and accepting routes as a vec? (complexity/performance issues) - would need to revamp gitlab logic too
- Improve our errors.
- Documentation refresh.
- Look into query/headers/...
3 changes: 3 additions & 0 deletions docs/Authority.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Authority Router

TODO
3 changes: 3 additions & 0 deletions docs/Method.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Method Router

TODO
3 changes: 3 additions & 0 deletions docs/Path.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Path Router

TODO
5 changes: 3 additions & 2 deletions src/chain.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use crate::{MethodId, PathId};
use crate::{AuthorityId, MethodId, PathId};
use std::fmt::Display;

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct DataChain {
pub authority: AuthorityId,
pub path: PathId,
pub method: MethodId,
}

impl Display for DataChain {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}-{}", self.path, self.method)
write!(f, "{}-{}-{}", self.authority, self.path, self.method)
}
}
60 changes: 4 additions & 56 deletions src/decode.rs
Original file line number Diff line number Diff line change
@@ -1,57 +1,5 @@
use crate::errors::EncodingError;
use std::borrow::Cow;
mod percent;
pub use percent::percent_decode;

/// Try and percent-decode input bytes.
/// Does not do any sort of normalization, simply decodes hex characters.
pub fn percent_decode(input: &[u8]) -> Result<Cow<'_, [u8]>, EncodingError> {
if !input.contains(&b'%') {
return Ok(Cow::Borrowed(input));
}

let mut output = Vec::with_capacity(input.len());
let mut i = 0;
let len = input.len();

while i < len {
if input[i] == b'%' && i + 2 < len {
let a = input[i + 1];
let b = input[i + 2];

if let Some(decoded) = decode_hex(a, b) {
output.push(decoded);
} else {
return Err(EncodingError::InvalidEncoding {
input: String::from_utf8_lossy(input).to_string(),
position: i,
character: [b'%', a, b],
});
}

i += 3;
} else {
output.push(input[i]);
i += 1;
}
}

Ok(Cow::Owned(output))
}

#[inline]
const fn decode_hex(a: u8, b: u8) -> Option<u8> {
let high = match a {
b'0'..=b'9' => a - b'0',
b'A'..=b'F' => a - b'A' + 10,
b'a'..=b'f' => a - b'a' + 10,
_ => return None,
};

let low = match b {
b'0'..=b'9' => b - b'0',
b'A'..=b'F' => b - b'A' + 10,
b'a'..=b'f' => b - b'a' + 10,
_ => return None,
};

Some((high << 4) | low)
}
mod punycode;
pub use punycode::punycode_decode;
Loading

0 comments on commit 6df8ad1

Please sign in to comment.