pub struct LinkedList<M: AnyFrameMeta + Repr<MetaSlotSmall>> {
pub front: Option<ReprPtr<MetaSlot, MetadataAsLink<M>>>,
pub back: Option<ReprPtr<MetaSlot, MetadataAsLink<M>>>,
pub size: usize,
pub list_id: u64,
}Expand description
A linked list of frames.
Two key features that LinkedList is different from
alloc::collections::LinkedList is that:
- It is intrusive, meaning that the links are part of the frame metadata. This allows the linked list to be used without heap allocation. But it disallows a frame to be in multiple linked lists at the same time.
- The linked list exclusively own the frames, meaning that it takes
unique pointers
UniqueFrame. And other bodies cannot [from_in_use] a frame that is inside a linked list. - We also allow creating cursors at a specific frame, allowing $O(1)$ removal without iterating through the list at a cost of some checks.
§Verified Properties
§Verification Design
The linked list is abstractly represented by a LinkedListOwner:
tracked struct LinkedListOwner<M: AnyFrameMeta + Repr<MetaSlotStorage>> {
pub list: Seq<LinkOwner>,
pub perms: Map<int, vstd_extra::cast_ptr::PointsTo<MetaSlot, Metadata<Link<M>>>>,
pub list_id: u64,
}§Invariant
The linked list uniquely owns the raw frames that it contains, so they cannot be used by other
data structures. The frame metadata field in_list is equal to list_id for all links in the list.
pub open spec fn inv_at(self, i: int) -> bool {
&&& self.perms.contains_key(i)
&&& self.perms[i].addr() == self.list[i].paddr
&&& self.perms[i].is_init()
&&& self.perms[i].value().wf(self.list[i])
&&& i == 0 <==> self.perms[i].mem_contents().value().prev is None
&&& i == self.list.len() - 1 <==> self.perms[i].value().next is None
&&& 0 < i ==> self.perms[i].value().prev is Some && self.perms[i].value().prev.unwrap()
== self.perms[i - 1].pptr()
&&& i < self.list.len() - 1 ==> self.perms[i].value().next is Some
&& self.perms[i].value().next.unwrap() == self.perms[i + 1].pptr()
&&& self.list[i].inv()
&&& self.list[i].in_list == self.list_id
}
pub open spec fn inv(self) -> bool {
forall|i: int| 0 <= i < self.list.len() ==> self.inv_at(i)
}§Safety
A given linked list can only have one cursor at a time, so there are no data races.
The prev and next fields of the metadata for each link always points to valid
links in the list, so the structure is memory safe (will not read or write invalid memory).
Fields§
§front: Option<ReprPtr<MetaSlot, MetadataAsLink<M>>>§back: Option<ReprPtr<MetaSlot, MetadataAsLink<M>>>§size: usizeThe number of frames in the list.
list_id: u64A lazily initialized ID, used to check whether a frame is in the list. 0 means uninitialized.
Implementations§
Source§impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> LinkedList<M>
impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> LinkedList<M>
Source§impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> LinkedList<M>
impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> LinkedList<M>
Sourcepub exec fn size(&self) -> s : usize
pub exec fn size(&self) -> s : usize
Tracked(owner): Tracked<LinkedListOwner<M>>,requiresself.wf(owner),owner.inv(),ensuress == self.model(owner).list.len(),Gets the number of frames in the linked list.
Sourcepub exec fn is_empty(&self) -> b : bool
pub exec fn is_empty(&self) -> b : bool
Tracked(owner): Tracked<LinkedListOwner<M>>,requiresself.wf(owner),owner.inv(),ensuresb ==> self.size == 0 && self.front is None && self.back is None,!b ==> self.size > 0 && self.front is Some && self.back is Some,Tells if the linked list is empty.
Sourcepub exec fn push_front(ptr: PPtr<Self>, frame: UniqueFrame<Link<M>>)
pub exec fn push_front(ptr: PPtr<Self>, frame: UniqueFrame<Link<M>>)
Tracked(regions): Tracked<&mut MetaRegionOwners>,
Tracked(owner): Tracked<&mut LinkedListOwner<M>>,
Tracked(perm): Tracked<vstd::simple_pptr::PointsTo<LinkedList<M>>>,
Tracked(frame_own): Tracked<&mut UniqueFrameOwner<Link<M>>>,requiresperm.pptr() == ptr,perm.is_init(),perm.mem_contents().value().wf(*old(owner)),old(owner).inv(),old(owner).list_id != 0,old(frame_own).inv(),old(frame_own).global_inv(*old(regions)),frame.wf(*old(frame_own)),old(frame_own).frame_link_inv(),old(owner).list.len() < usize::MAX,old(regions).inv(),old(regions).slots.contains_key(old(frame_own).slot_index),old(regions)
.slot_owners[old(frame_own).slot_index]
.inner_perms
.in_list
.is_for(old(regions).slots[old(frame_own).slot_index].value().in_list),ensuresowner.inv(),owner.list == old(owner).list.insert(0, frame_own.meta_own),owner.list_id == old(owner).list_id,frame_own.meta_own.paddr == old(frame_own).meta_own.paddr,frame_own.meta_own.in_list == old(owner).list_id,Pushes a frame to the front of the linked list.
§Verified Properties
§Preconditions
The list must be well-formed, with the pointers to its links’ metadata slots
matching the tracked permission objects. The new frame must be active, so that it is
valid to call into_raw on it inside of insert_before.
§Postconditions
The new frame is inserted at the front of the list, and the cursor is moved to the new frame. The list invariants are preserved.
§Safety
See [insert_before] for the safety guarantees.
Sourcepub exec fn pop_front(
ptr: PPtr<Self>,
) -> r : Option<(UniqueFrame<Link<M>>, Tracked<UniqueFrameOwner<Link<M>>>)>
pub exec fn pop_front( ptr: PPtr<Self>, ) -> r : Option<(UniqueFrame<Link<M>>, Tracked<UniqueFrameOwner<Link<M>>>)>
Tracked(regions): Tracked<&mut MetaRegionOwners>,
Tracked(perm): Tracked<vstd::simple_pptr::PointsTo<LinkedList<M>>>,
Tracked(owner): Tracked<LinkedListOwner<M>>,
Tracked(frame_own): Tracked<UniqueFrameOwner<Link<M>>>,requiresold(regions).inv(),perm.pptr() == ptr,perm.is_init(),perm.value().wf(owner),owner.inv(),old(regions).slots.contains_key(frame_to_index(owner.list[0].paddr)),ensuresowner.list.len() == 0 ==> r.is_none(),r.is_some() ==> r.unwrap().0.model(r.unwrap().1@).meta == owner.list[0]@,r.is_some() ==> r.unwrap().1@.frame_link_inv(),Pops a frame from the front of the linked list.
§Verified Properties
§Preconditions
The list must be well-formed, with the pointers to its links’ metadata slots matching the tracked permission objects. The list must be non-empty, so that the current frame is valid.
§Postconditions
The front frame is removed from the list, and the cursor is moved to the next frame. The list invariants are preserved.
§Safety
See [take_current] for the safety guarantees.
Sourcepub exec fn push_back(ptr: PPtr<Self>, frame: UniqueFrame<Link<M>>)
pub exec fn push_back(ptr: PPtr<Self>, frame: UniqueFrame<Link<M>>)
Tracked(regions): Tracked<&mut MetaRegionOwners>,
Tracked(owner): Tracked<&mut LinkedListOwner<M>>,
Tracked(perm): Tracked<vstd::simple_pptr::PointsTo<LinkedList<M>>>,
Tracked(frame_own): Tracked<&mut UniqueFrameOwner<Link<M>>>,requiresperm.pptr() == ptr,perm.is_init(),perm.mem_contents().value().wf(*old(owner)),old(owner).inv(),old(owner).list_id != 0,old(frame_own).inv(),old(frame_own).global_inv(*old(regions)),frame.wf(*old(frame_own)),old(frame_own).frame_link_inv(),old(owner).list.len() < usize::MAX,old(regions).inv(),old(regions).slots.contains_key(old(frame_own).slot_index),old(regions)
.slot_owners[old(frame_own).slot_index]
.inner_perms
.in_list
.is_for(old(regions).slots[old(frame_own).slot_index].value().in_list),ensuresowner.inv(),old(owner).list.len() > 0
==> owner.list
== old(owner).list.insert(old(owner).list.len() as int - 1, frame_own.meta_own),old(owner).list.len() == 0
==> owner.list == old(owner).list.insert(0, frame_own.meta_own),owner.list_id == old(owner).list_id,frame_own.meta_own.paddr == old(frame_own).meta_own.paddr,frame_own.meta_own.in_list == old(owner).list_id,Pushes a frame to the back of the linked list.
§Verified Properties
§Preconditions
The list must be well-formed, with the pointers to its links’ metadata slots
matching the tracked permission objects. The new frame must be active, so that it is
valid to call into_raw on it inside of insert_before.
§Postconditions
- The new frame is inserted at the back of the list, and the cursor is moved to the new frame.
- The list invariants are preserved.
§Safety
See [insert_before] for the safety guarantees.
Sourcepub exec fn pop_back(
ptr: PPtr<Self>,
) -> r : Option<(UniqueFrame<Link<M>>, Tracked<UniqueFrameOwner<Link<M>>>)>
pub exec fn pop_back( ptr: PPtr<Self>, ) -> r : Option<(UniqueFrame<Link<M>>, Tracked<UniqueFrameOwner<Link<M>>>)>
Tracked(regions): Tracked<&mut MetaRegionOwners>,
Tracked(perm): Tracked<vstd::simple_pptr::PointsTo<LinkedList<M>>>,
Tracked(owner): Tracked<LinkedListOwner<M>>,
Tracked(frame_own): Tracked<UniqueFrameOwner<Link<M>>>,requiresold(regions).inv(),perm.pptr() == ptr,perm.is_init(),perm.mem_contents().value().wf(owner),owner.inv(),old(regions).slots.contains_key(frame_to_index(owner.list[owner.list.len() - 1].paddr)),ensuresowner.list.len() == 0 ==> r.is_none(),r.is_some()
==> r.unwrap().0.model(r.unwrap().1@).meta == owner.list[owner.list.len() - 1]@,r.is_some() ==> r.unwrap().1@.frame_link_inv(),Pops a frame from the back of the linked list.
§Verified Properties
§Preconditions
- The list must be well-formed, with the pointers to its links’ metadata slots matching the tracked permission objects.
- The list must be non-empty, so that the current frame is valid.
§Postconditions
- The back frame is removed from the list, and the cursor is moved to the “ghost” non-element.
- The list invariants are preserved.
§Safety
See [take_current] for the safety guarantees.
Sourcepub exec fn contains(ptr: PPtr<Self>, frame: Paddr) -> r : bool
pub exec fn contains(ptr: PPtr<Self>, frame: Paddr) -> r : bool
Tracked(regions): Tracked<&mut MetaRegionOwners>,
Tracked(slot_own): Tracked<&MetaSlotOwner>,
Tracked(owner): Tracked<&mut LinkedListOwner<M>>,requiresslot_own.inv(),old(regions).inv(),old(regions).slots.contains_key(frame_to_index(frame)),old(regions).slots[frame_to_index(frame)].is_init(),old(regions).slot_owners.contains_key(frame_to_index(frame)),old(regions)
.slot_owners[frame_to_index(frame)]
.inner_perms
.in_list
.is_for(old(regions).slots[frame_to_index(frame)].mem_contents().value().in_list),ensuresold(owner).list_id != 0 ==> *owner == *old(owner),Tells if a frame is in the list.
§Verified Properties
§Preconditions
- The list must be well-formed, with the pointers to its links’ metadata slots matching the tracked permission objects.
- The frame must be a valid, active frame.
§Postconditions
The function returns true if the frame is in the list, false otherwise.
§Safety
lazy_get_iduses atomic memory accesses, so there are no data races.- We assume that the ID allocator has an available ID if the list previously didn’t have one, but the consequence if that is not the case is a failsafe panic.
- Everything else conforms to the safe interface.
Sourcepub exec fn cursor_mut_at(ptr: PPtr<Self>, frame: Paddr) -> r : Option<CursorMut<M>>
pub exec fn cursor_mut_at(ptr: PPtr<Self>, frame: Paddr) -> r : Option<CursorMut<M>>
Tracked(regions): Tracked<&mut MetaRegionOwners>,
Tracked(owner): Tracked<LinkedListOwner<M>>,
Tracked(perm): Tracked<vstd::simple_pptr::PointsTo<LinkedList<M>>>,
->
cursor_owner: Tracked<Option<CursorOwner<M>>>,requiresold(regions).inv(),ensures!has_safe_slot(frame) ==> r is None,Gets a cursor at the specified frame if the frame is in the list.
This method fails if the frame is not in the list.
§Verified Properties
§Preconditions
- The list must be well-formed, with the pointers to its links’ metadata slots matching the tracked permission objects.
- The frame should be raw (because it is owned by the list)
§Postconditions
- This functions post-conditions are incomplete due to refactoring of the permission model. When complete, it will guarantee that the cursor is well-formed and points to the matching element in the list.
§Safety
lazy_get_iduses atomic memory accesses, so there are no data races.- We assume that the ID allocator has an available ID if the list previously didn’t have one, but the consequence if that is not the case is a failsafe panic.
- Everything else conforms to the safe interface.
Sourcepub exec fn cursor_front_mut(ptr: PPtr<Self>) -> r : CursorMut<M>
pub exec fn cursor_front_mut(ptr: PPtr<Self>) -> r : CursorMut<M>
Tracked(owner): Tracked<LinkedListOwner<M>>,
Tracked(perm): Tracked<vstd::simple_pptr::PointsTo<LinkedList<M>>>,
->
r_perm: Tracked<CursorOwner<M>>,requiresperm.pptr() == ptr,perm.is_init(),perm.mem_contents().value().wf(owner),owner.inv(),ensuresr.wf(r_perm@),r_perm@.inv(),r_perm@ == CursorOwner::front_owner_spec(owner, perm),Gets a cursor at the front that can mutate the linked list links.
If the list is empty, the cursor points to the “ghost” non-element.
§Verified Properties
§Preconditions
- The list must be well-formed, with the pointers to its links’ metadata slots matching the tracked permission objects.
§Postconditions
- The cursor is well-formed, with the pointers to its links’ metadata slots matching the tracked permission objects. The list invariants are preserved.
- See
CursorOwner::front_owner_specfor the precise specification.
§Safety
- This function only uses the list permission, so there are no illegal memory accesses.
- No data races are possible.
Sourcepub exec fn cursor_back_mut(
ptr: PPtr<Self>,
) -> res : (CursorMut<M>, Tracked<CursorOwner<M>>)
pub exec fn cursor_back_mut( ptr: PPtr<Self>, ) -> res : (CursorMut<M>, Tracked<CursorOwner<M>>)
Tracked(owner): Tracked<LinkedListOwner<M>>,
Tracked(perm): Tracked<vstd::simple_pptr::PointsTo<LinkedList<M>>>,requiresperm.pptr() == ptr,perm.is_init(),perm.mem_contents().value().wf(owner),owner.inv(),ensuresres.0.wf(res.1@),res.1@.inv(),res.1@ == CursorOwner::back_owner_spec(owner, perm),Gets a cursor at the back that can mutate the linked list links.
If the list is empty, the cursor points to the “ghost” non-element.
§Verified Properties
§Preconditions
- The list must be well-formed, with the pointers to its links’ metadata slots matching the tracked permission objects.
§Postconditions
- The cursor is well-formed, with the pointers to its links’ metadata slots
matching the tracked permission objects. The list invariants are preserved.
See
CursorOwner::back_owner_specfor the precise specification.
§Safety
- This function only uses the list permission, so there are no illegal memory accesses.
- No data races are possible.
Trait Implementations§
Source§impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> Default for LinkedList<M>
impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> Default for LinkedList<M>
Source§impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> ModelOf for LinkedList<M>
impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> ModelOf for LinkedList<M>
Source§impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> OwnerOf for LinkedList<M>
impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> OwnerOf for LinkedList<M>
Source§open spec fn wf(self, owner: Self::Owner) -> bool
open spec fn wf(self, owner: Self::Owner) -> bool
{
&&& self.front is None <==> owner.list.len() == 0
&&& self.back is None <==> owner.list.len() == 0
&&& owner.list.len() > 0
==> self.front is Some && self.front.unwrap().addr() == owner.list[0].paddr
&& owner.perms[0].pptr().addr() == self.front.unwrap().addr()
&& self.front.unwrap().ptr == owner.perms[0].points_to.pptr()
&& self.back is Some
&& self.back.unwrap().addr() == owner.list[owner.list.len() - 1].paddr
&& owner.perms[owner.list.len() - 1].pptr().addr()
== self.back.unwrap().addr()
&& self.back.unwrap().ptr
== owner.perms[owner.list.len() - 1].points_to.pptr()
&&& self.size == owner.list.len()
&&& self.list_id == owner.list_id
}Source§type Owner = LinkedListOwner<M>
type Owner = LinkedListOwner<M>
Inv, indicating that it must
has a consistent state.