Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Zero-copy Path implementation #25

Merged
merged 14 commits into from
Jul 20, 2024
7 changes: 7 additions & 0 deletions Cargo.lock

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

5 changes: 4 additions & 1 deletion data-model/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@ version = "0.1.0"
edition = "2021"

[dependencies]
arbitrary = { version = "1.0.2", features = ["derive"]}
arbitrary = { version = "1.0.2", features = [
"derive",
] } # TODO feature-gate this
bytes = "1.6.0"
44 changes: 25 additions & 19 deletions data-model/src/entry.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{
use crate::{
parameters::{IsAuthorisedWrite, NamespaceId, PayloadDigest, SubspaceId},
path::Path,
};
Expand All @@ -18,19 +18,18 @@ pub type Timestamp = u64;
/// - `P` - The type used for [`Path`]s.
/// - `PD` - The type used for [`PayloadDigest`].
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Entry<N, S, P, PD>
pub struct Entry<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD>
where
N: NamespaceId,
S: SubspaceId,
P: Path,
PD: PayloadDigest,
{
/// The identifier of the namespace to which the [`Entry`] belongs.
pub namespace_id: N,
/// The identifier of the subspace to which the [`Entry`] belongs.
pub subspace_id: S,
/// The [`Path`] to which the [`Entry`] was written.
pub path: P,
pub path: Path<MCL, MCC, MPL>,
/// The claimed creation time of the [`Entry`].
pub timestamp: Timestamp,
/// The length of the Payload in bytes.
Expand All @@ -39,11 +38,10 @@ where
pub payload_digest: PD,
}

impl<N, S, P, PD> Entry<N, S, P, PD>
impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> Entry<MCL, MCC, MPL, N, S, PD>
where
N: NamespaceId,
S: SubspaceId,
P: Path,
PD: PayloadDigest,
{
/// Return if this [`Entry`] is newer than another using their timestamps.
Expand Down Expand Up @@ -72,23 +70,25 @@ pub struct UnauthorisedWriteError;
/// - `P` - The type used for [`Path`]s.
/// - `PD` - The type used for [`PayloadDigest`].
/// - `AT` - The type used for the [`AuthorisationToken` (willowprotocol.org)](https://willowprotocol.org/specs/data-model/index.html#AuthorisationToken).
pub struct AuthorisedEntry<N, S, P, PD, AT>(pub Entry<N, S, P, PD>, pub AT)
pub struct AuthorisedEntry<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD, AT>(
pub Entry<MCL, MCC, MPL, N, S, PD>,
pub AT,
)
where
N: NamespaceId,
S: SubspaceId,
P: Path,
PD: PayloadDigest;

impl<N, S, P, PD, AT> AuthorisedEntry<N, S, P, PD, AT>
impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD, AT>
AuthorisedEntry<MCL, MCC, MPL, N, S, PD, AT>
where
N: NamespaceId,
S: SubspaceId,
P: Path,
PD: PayloadDigest,
{
/// Construct an [`AuthorisedEntry`] if the token permits the writing of this entry, otherwise return an [`UnauthorisedWriteError`]
pub fn new<IAW: IsAuthorisedWrite<N, S, P, PD, AT>>(
entry: Entry<N, S, P, PD>,
pub fn new<IAW: IsAuthorisedWrite<MCL, MCC, MPL, N, S, PD, AT>>(
entry: Entry<MCL, MCC, MPL, N, S, PD>,
token: AT,
is_authorised_write: IAW,
) -> Result<Self, UnauthorisedWriteError> {
Expand All @@ -102,7 +102,7 @@ where

#[cfg(test)]
mod tests {
use crate::path::{PathComponent, PathComponentBox, PathRc};
use crate::path::Component;

use super::*;

Expand Down Expand Up @@ -131,7 +131,8 @@ mod tests {
let e_a1 = Entry {
namespace_id: FakeNamespaceId::default(),
subspace_id: FakeSubspaceId::default(),
path: PathRc::<MCL, MCC, MPL>::new(&[PathComponentBox::new(&[b'a']).unwrap()]).unwrap(),
path: Path::<MCL, MCC, MPL>::new_from_slice(&[Component::new(&[b'a']).unwrap()])
.unwrap(),
payload_digest: FakePayloadDigest::default(),
payload_length: 0,
timestamp: 20,
Expand All @@ -140,7 +141,8 @@ mod tests {
let e_a2 = Entry {
namespace_id: FakeNamespaceId::default(),
subspace_id: FakeSubspaceId::default(),
path: PathRc::<MCL, MCC, MPL>::new(&[PathComponentBox::new(&[b'a']).unwrap()]).unwrap(),
path: Path::<MCL, MCC, MPL>::new_from_slice(&[Component::new(&[b'a']).unwrap()])
.unwrap(),
payload_digest: FakePayloadDigest::default(),
payload_length: 0,
timestamp: 10,
Expand All @@ -151,7 +153,8 @@ mod tests {
let e_b1 = Entry {
namespace_id: FakeNamespaceId::default(),
subspace_id: FakeSubspaceId::default(),
path: PathRc::<MCL, MCC, MPL>::new(&[PathComponentBox::new(&[b'a']).unwrap()]).unwrap(),
path: Path::<MCL, MCC, MPL>::new_from_slice(&[Component::new(&[b'a']).unwrap()])
.unwrap(),
payload_digest: FakePayloadDigest(2),
payload_length: 0,
timestamp: 10,
Expand All @@ -160,7 +163,8 @@ mod tests {
let e_b2 = Entry {
namespace_id: FakeNamespaceId::default(),
subspace_id: FakeSubspaceId::default(),
path: PathRc::<MCL, MCC, MPL>::new(&[PathComponentBox::new(&[b'a']).unwrap()]).unwrap(),
path: Path::<MCL, MCC, MPL>::new_from_slice(&[Component::new(&[b'a']).unwrap()])
.unwrap(),
payload_digest: FakePayloadDigest(1),
payload_length: 0,
timestamp: 10,
Expand All @@ -171,7 +175,8 @@ mod tests {
let e_c1 = Entry {
namespace_id: FakeNamespaceId::default(),
subspace_id: FakeSubspaceId::default(),
path: PathRc::<MCL, MCC, MPL>::new(&[PathComponentBox::new(&[b'a']).unwrap()]).unwrap(),
path: Path::<MCL, MCC, MPL>::new_from_slice(&[Component::new(&[b'a']).unwrap()])
.unwrap(),
payload_digest: FakePayloadDigest::default(),
payload_length: 2,
timestamp: 20,
Expand All @@ -180,7 +185,8 @@ mod tests {
let e_c2 = Entry {
namespace_id: FakeNamespaceId::default(),
subspace_id: FakeSubspaceId::default(),
path: PathRc::<MCL, MCC, MPL>::new(&[PathComponentBox::new(&[b'a']).unwrap()]).unwrap(),
path: Path::<MCL, MCC, MPL>::new_from_slice(&[Component::new(&[b'a']).unwrap()])
.unwrap(),
payload_digest: FakePayloadDigest::default(),
payload_length: 1,
timestamp: 20,
Expand Down
59 changes: 29 additions & 30 deletions data-model/src/grouping/area.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,22 @@ impl<S: SubspaceId> AreaSubspace<S> {
/// A grouping of entries.
/// [Definition](https://willowprotocol.org/specs/grouping-entries/index.html#areas).
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Area<S: SubspaceId, P: Path> {
pub struct Area<const MCL: usize, const MCC: usize, const MPL: usize, S: SubspaceId> {
/// To be included in this [`Area`], an [`Entry`]’s `subspace_id` must be equal to the subspace_id, unless it is any.
pub subspace: AreaSubspace<S>,
/// To be included in this [`Area`], an [`Entry`]’s `path` must be prefixed by the path.
pub path: P,
pub path: Path<MCL, MCC, MPL>,
/// To be included in this [`Area`], an [`Entry`]’s `timestamp` must be included in the times.
pub times: Range<Timestamp>,
}

impl<S: SubspaceId, P: Path> Area<S, P> {
impl<const MCL: usize, const MCC: usize, const MPL: usize, S: SubspaceId> Area<MCL, MCC, MPL, S> {
/// Return an [`Area`] which includes all entries.
/// [Definition](https://willowprotocol.org/specs/grouping-entries/index.html#full_area).
pub fn full() -> Self {
Self {
subspace: AreaSubspace::Any,
path: P::empty(),
path: Path::new_empty(),
times: Range::new_open(0),
}
}
Expand All @@ -64,15 +64,15 @@ impl<S: SubspaceId, P: Path> Area<S, P> {
pub fn subspace(sub: S) -> Self {
Self {
subspace: AreaSubspace::Id(sub),
path: P::empty(),
path: Path::new_empty(),
times: Range::new_open(0),
}
}

/// Return whether an [`Area`] [includes](https://willowprotocol.org/specs/grouping-entries/index.html#area_include) an [`Entry`].
pub fn includes_entry<N: NamespaceId, PD: PayloadDigest>(
&self,
entry: &Entry<N, S, P, PD>,
entry: &Entry<MCL, MCC, MPL, N, S, PD>,
) -> bool {
self.subspace.includes(&entry.subspace_id)
&& self.path.is_prefix_of(&entry.path)
Expand All @@ -81,7 +81,7 @@ impl<S: SubspaceId, P: Path> Area<S, P> {

/// Return the intersection of this [`Area`] with another.
/// [Definition](https://willowprotocol.org/specs/grouping-entries/index.html#area_intersection).
pub fn intersection(&self, other: &Area<S, P>) -> Option<Self> {
pub fn intersection(&self, other: &Area<MCL, MCC, MPL, S>) -> Option<Self> {
let subspace_id = self.subspace.intersection(&other.subspace)?;
let path = if self.path.is_prefix_of(&other.path) {
Some(other.path.clone())
Expand All @@ -102,7 +102,7 @@ impl<S: SubspaceId, P: Path> Area<S, P> {
#[cfg(test)]
mod tests {

use crate::path::{PathComponent, PathComponentBox, PathRc};
use crate::path::Component;

use super::*;

Expand Down Expand Up @@ -171,84 +171,83 @@ mod tests {

#[test]
fn area_full() {
let full_area = Area::<FakeSubspaceId, PathRc<MCL, MCC, MPL>>::full();
let full_area = Area::<MCL, MCC, MPL, FakeSubspaceId>::full();

assert_eq!(
full_area,
Area {
subspace: AreaSubspace::Any,
path: PathRc::empty(),
path: Path::new_empty(),
times: Range::new_open(0)
}
)
}

#[test]
fn area_subspace() {
let subspace_area =
Area::<FakeSubspaceId, PathRc<MCL, MCC, MPL>>::subspace(FakeSubspaceId(7));
let subspace_area = Area::<MCL, MCC, MPL, FakeSubspaceId>::subspace(FakeSubspaceId(7));

assert_eq!(
subspace_area,
Area {
subspace: AreaSubspace::Id(FakeSubspaceId(7)),
path: PathRc::empty(),
path: Path::new_empty(),
times: Range::new_open(0)
}
)
}

#[test]
fn area_intersects() {
let empty_intersection_subspace = Area::<FakeSubspaceId, PathRc<MCL, MCC, MPL>> {
let empty_intersection_subspace = Area::<MCL, MCC, MPL, FakeSubspaceId> {
subspace: AreaSubspace::Id(FakeSubspaceId(1)),
path: PathRc::empty(),
path: Path::new_empty(),
times: Range::new_open(0),
}
.intersection(&Area {
subspace: AreaSubspace::Id(FakeSubspaceId(2)),
path: PathRc::empty(),
path: Path::new_empty(),
times: Range::new_open(0),
});

assert!(empty_intersection_subspace.is_none());

let empty_intersection_path = Area::<FakeSubspaceId, PathRc<MCL, MCC, MPL>> {
let empty_intersection_path = Area::<MCL, MCC, MPL, FakeSubspaceId> {
subspace: AreaSubspace::Id(FakeSubspaceId(1)),
path: PathRc::new(&[PathComponentBox::new(&[b'0']).unwrap()]).unwrap(),
path: Path::new_from_slice(&[Component::new(&[b'0']).unwrap()]).unwrap(),
times: Range::new_open(0),
}
.intersection(&Area {
subspace: AreaSubspace::Id(FakeSubspaceId(1)),
path: PathRc::new(&[PathComponentBox::new(&[b'1']).unwrap()]).unwrap(),
path: Path::new_from_slice(&[Component::new(&[b'1']).unwrap()]).unwrap(),
times: Range::new_open(0),
});

assert!(empty_intersection_path.is_none());

let empty_intersection_time = Area::<FakeSubspaceId, PathRc<MCL, MCC, MPL>> {
let empty_intersection_time = Area::<MCL, MCC, MPL, FakeSubspaceId> {
subspace: AreaSubspace::Id(FakeSubspaceId(1)),
path: PathRc::empty(),
path: Path::new_empty(),
times: Range::new_closed(0, 1).unwrap(),
}
.intersection(&Area {
subspace: AreaSubspace::Id(FakeSubspaceId(1)),
path: PathRc::empty(),
path: Path::new_empty(),
times: Range::new_closed(2, 3).unwrap(),
});

assert!(empty_intersection_time.is_none());

let intersection = Area::<FakeSubspaceId, PathRc<MCL, MCC, MPL>> {
let intersection = Area::<MCL, MCC, MPL, FakeSubspaceId> {
subspace: AreaSubspace::Any,
path: PathRc::new(&[PathComponentBox::new(&[b'1']).unwrap()]).unwrap(),
path: Path::new_from_slice(&[Component::new(&[b'1']).unwrap()]).unwrap(),
times: Range::new_closed(0, 10).unwrap(),
}
.intersection(&Area {
subspace: AreaSubspace::Id(FakeSubspaceId(1)),
path: PathRc::new(&[
PathComponentBox::new(&[b'1']).unwrap(),
PathComponentBox::new(&[b'2']).unwrap(),
path: Path::new_from_slice(&[
Component::new(&[b'1']).unwrap(),
Component::new(&[b'2']).unwrap(),
])
.unwrap(),
times: Range::new_closed(5, 15).unwrap(),
Expand All @@ -260,9 +259,9 @@ mod tests {
intersection.unwrap(),
Area {
subspace: AreaSubspace::Id(FakeSubspaceId(1)),
path: PathRc::new(&[
PathComponentBox::new(&[b'1']).unwrap(),
PathComponentBox::new(&[b'2']).unwrap(),
path: Path::new_from_slice(&[
Component::new(&[b'1']).unwrap(),
Component::new(&[b'2']).unwrap(),
])
.unwrap(),
times: Range::new_closed(5, 10).unwrap(),
Expand Down
15 changes: 10 additions & 5 deletions data-model/src/grouping/area_of_interest.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
use crate::{grouping::area::Area, parameters::SubspaceId, path::Path};
use crate::{grouping::area::Area, parameters::SubspaceId};

/// A grouping of [`Entry`]s that are among the newest in some [`Store`].
/// [Definition](https://willowprotocol.org/specs/grouping-entries/index.html#aois).
pub struct AreaOfInterest<S: SubspaceId, P: Path> {
pub struct AreaOfInterest<const MCL: usize, const MCC: usize, const MPL: usize, S: SubspaceId> {
/// To be included in this [`AreaOfInterest`], an [`Entry`] must be included in the [`Area`].
pub area: Area<S, P>,
pub area: Area<MCL, MCC, MPL, S>,
/// To be included in this AreaOfInterest, an Entry’s timestamp must be among the max_count greatest Timestamps, unless max_count is zero.
pub max_count: u64,
/// The total payload_lengths of all included Entries is at most max_size, unless max_size is zero.
pub max_size: u64,
}

impl<S: SubspaceId, P: Path> AreaOfInterest<S, P> {
impl<const MCL: usize, const MCC: usize, const MPL: usize, S: SubspaceId>
AreaOfInterest<MCL, MCC, MPL, S>
{
/// Return the intersection of this [`AreaOfInterest`] with another.
/// [Definition](https://willowprotocol.org/specs/grouping-entries/index.html#aoi_intersection).
pub fn intersection(&self, other: AreaOfInterest<S, P>) -> Option<AreaOfInterest<S, P>> {
pub fn intersection(
&self,
other: AreaOfInterest<MCL, MCC, MPL, S>,
) -> Option<AreaOfInterest<MCL, MCC, MPL, S>> {
match self.area.intersection(&other.area) {
None => None,
Some(area_intersection) => Some(Self {
Expand Down
Loading