Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,10 @@ assert_eq!(trie.lookup(u32::from_be_bytes([192, 168, 1, 5])), Some(&"192.168.0.0
| Function | Description |
|---|---|
| `new()` | Construct a new, empty poptrie |
| `insert(key, key_length, value)` | Insert a value associated with the given prefix |
| `lookup(key)` | Longest-prefix match lookup, returns `Option<&V>` |
| `contains_key(key, key_length)` | Returns `true` if the exact prefix is present |
| `remove(key, key_length)` | Remove a prefix and return its value |
| `insert(prefix, value)` | Insert a value associated with the given prefix |
| `lookup(address)` | Longest-prefix match lookup, returns `Option<&V>` |
| `contains_key(prefix)` | Returns `true` if the exact prefix is present |
| `remove(prefix)` | Remove a prefix and return its value |

### Lookup performance

Expand Down
3 changes: 1 addition & 2 deletions benches/lpm_benches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,7 @@ fn bench_insert(c: &mut Criterion) {
let mut poptrie = Poptrie::new();
for &((prefix, length), val) in prefixes {
poptrie.insert(
black_box(prefix),
black_box(length),
black_box((prefix, length)),
black_box(val),
);
}
Expand Down
47 changes: 26 additions & 21 deletions src/key.rs → src/address.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
#![allow(clippy::unusual_byte_groupings)] // Grouped 6 by 6 because that's the current STRIDE
use core::ops::{Shl, Shr};

/// A trait for types that can be used as keys in the [poptrie](crate::Poptrie).
/// A trait for types that can be used as addresses in the `Prefix`.
///
/// This is currently not sealed to allow for custom `Key` types.
/// This is currently not sealed to allow for custom types.
/// Future versions may change this, which would be a breaking change.
///
/// - Needs to inform its bit width and a `to_u8` method that returns its 8 least significant bits.
/// - Needs to implement `rotate_right` method that rotates the key by `n` bits to the right.
///
/// # Implementation Details
///
/// The current bounds are required to guarantee performance, else there would be a more flexible
/// trait with their own bit extraction functions. This may change in the future.
///
/// # Examples
///
/// Implementing `Key` for a custom newtype:
/// Implementing `Address` for a custom newtype:
/// ```
/// use poptrie::Key;
/// use poptrie::Address;
/// use core::ops::{Shl, Shr};
///
/// #[derive(Clone, Copy)]
Expand All @@ -29,7 +34,7 @@ use core::ops::{Shl, Shr};
/// fn shr(self, n: u8) -> Self { MyAddr(self.0 >> n) }
/// }
///
/// impl Key for MyAddr {
/// impl Address for MyAddr {
/// const BITS: u8 = 32;
/// fn to_u8(self) -> u8 { self.0 as u8 }
/// fn rotate_right(self, n: u32) -> Self { MyAddr(self.0.rotate_right(n)) }
Expand All @@ -38,18 +43,18 @@ use core::ops::{Shl, Shr};
/// assert_eq!(MyAddr::BITS, 32);
/// assert_eq!(MyAddr(0xdeadbeef).to_u8(), 0xef);
/// ```
pub trait Key:
pub trait Address:
Copy + Shl<u8, Output = Self> + Shr<u8, Output = Self> + Sized
{
/// The number of bits in the key.
/// The number of bits in the address.
const BITS: u8;
/// Converts the least significant bits of the key to a u8.
/// Converts the least significant bits of the address to a u8.
fn to_u8(self) -> u8;
/// Rotates the key to the right by `n` bits.
/// Rotates the address to the right by `n` bits.
fn rotate_right(self, n: u32) -> Self;
}

impl Key for u32 {
impl Address for u32 {
const BITS: u8 = 32;
#[inline(always)]
fn to_u8(self) -> u8 {
Expand All @@ -62,7 +67,7 @@ impl Key for u32 {
}
}

impl Key for u128 {
impl Address for u128 {
const BITS: u8 = 128;
#[inline(always)]
fn to_u8(self) -> u8 {
Expand All @@ -84,19 +89,19 @@ impl Key for u128 {
/// ^^^^^^^^^^^^^^
/// extracted bits
///```
/// If `len` + `offset` > K::BITS, the extraction will be zero-padded from the right:
/// If `len` + `offset` > A::BITS, the extraction will be zero-padded from the right:
///``` text
/// MSB |------------------------key-------------------------| LSB
/// |---------------- offset -----------------|----- len ----|
/// |^^^^^^^^^^0000|
/// extracted bits
///```
#[inline(always)]
pub(crate) fn extract_bits<K>(key: K, offset: u8, len: u8) -> u8
pub(crate) fn extract_bits<A>(address: A, offset: u8, len: u8) -> u8
where
K: Key,
A: Address,
{
(key.rotate_right((K::BITS - offset).wrapping_sub(len) as u32)).to_u8()
(address.rotate_right((A::BITS - offset).wrapping_sub(len) as u32)).to_u8()
& ((1u16 << len) - 1) as u8
}

Expand All @@ -110,7 +115,7 @@ where
/// ^^^^^^^^^^^^^^
/// extracted bits
///```
/// If `len` + `offset` > `K::BITS`, the extraction will be saturated:
/// If `len` + `offset` > `A::BITS`, the extraction will be saturated:
///
/// ``` text
/// MSB |------------------------key-------------------------| LSB
Expand All @@ -119,16 +124,16 @@ where
/// extracted bits
///```
#[inline(always)]
pub(crate) fn extract_bits_saturated<K>(key: K, offset: u8, len: u8) -> u8
pub(crate) fn extract_bits_saturated<A>(address: A, offset: u8, len: u8) -> u8
where
K: Key,
A: Address,
{
// TODO: Check if mask version is faster
let remaining = u8::saturating_sub(K::BITS - offset, len);
if remaining + offset == K::BITS {
let remaining = u8::saturating_sub(A::BITS - offset, len);
if remaining + offset == A::BITS {
return 0;
}
(key << offset >> (remaining + offset)).to_u8()
(address << offset >> (remaining + offset)).to_u8()
}

#[cfg(test)]
Expand Down
24 changes: 14 additions & 10 deletions src/bitmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use alloc::{collections::btree_map::BTreeMap, vec::Vec};

use crate::{
STRIDE,
key::{Key, extract_bits, extract_bits_saturated},
address::{Address, extract_bits, extract_bits_saturated},
};

/// A generic bitmap for storing u8 encoded ids of 0..63
Expand Down Expand Up @@ -79,8 +79,12 @@ impl PrefixId {
PrefixId((1u8 << len) - 1 + prefix)
}

pub(crate) fn from_key<K: Key>(key: K, key_offset: u8, stride: u8) -> Self {
let prefix = extract_bits_saturated(key, key_offset, stride);
pub(crate) fn from_address<A: Address>(
address: A,
offset: u8,
stride: u8,
) -> Self {
let prefix = extract_bits_saturated(address, offset, stride);
PrefixId::new(prefix, stride)
}

Expand Down Expand Up @@ -137,12 +141,12 @@ where
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) struct StrideId(pub(crate) u8);
impl StrideId {
pub(crate) fn from_key<K: Key>(
key: K,
key_offset: u8,
pub(crate) fn from_address<A: Address>(
address: A,
offset: u8,
length: u8,
) -> StrideId {
StrideId(extract_bits(key, key_offset, length))
StrideId(extract_bits(address, offset, length))
}
}

Expand Down Expand Up @@ -209,11 +213,11 @@ mod tests {

#[test]
fn test_prefix_to_stride() {
for key in 0u32..256 {
for address in 0u32..256 {
for stride in 0u8..STRIDE {
// PrefixIds are never full strides
let prefix = PrefixId::from_key(key, 0, stride);
let stride = StrideId::from_key(key, 0, stride);
let prefix = PrefixId::from_address(address, 0, stride);
let stride = StrideId::from_address(address, 0, stride);
assert_eq!(prefix.stride_id(), stride);
}
}
Expand Down
Loading